C โครงสร้างโปรแกรมและ printf
มาดูโปรแกรม C ทีละส่วนอย่างละเอียด
#include <stdio.h>
// นี่คือคอมเมนต์บรรทัดเดียว
/* คอมเมนต์
หลายบรรทัด */
int main(void)
{
int age = 30;
printf("อายุ %d ปี\n", age);
return 0;
}#include
#include <stdio.h>บอก preprocessor ให้ “แปะ” เนื้อหาของไฟล์ stdio.h ตรงนี้ ก่อนคอมไพล์จริง ไฟล์ .h (header) ส่วนใหญ่มีการ ประกาศ ฟังก์ชัน — บอกว่า printf รับอะไร คืนอะไร โดยไม่มีโค้ดจริง (โค้ดจริงอยู่ใน libc ที่ link ทีหลัง) header ยังใส่ #define มาโคร, typedef/struct/enum, และ static inline function ได้ด้วย (บทที่ 26)
#include <...>— header ของระบบ/ไลบรารีมาตรฐาน คอมไพเลอร์หาในที่ที่ติดตั้งไว้#include "..."— header ของเราเอง คอมไพเลอร์หาในโฟลเดอร์เดียวกับไฟล์ก่อน
header มาตรฐานที่เจอบ่อย: <stdio.h> (I/O), <stdlib.h> (malloc, exit, atoi), <string.h> (strlen, strcpy), <math.h> (sqrt, pow)
ฟังก์ชัน main
int main(void)
{
return 0;
}- ทุกโปรแกรม C เริ่มรันที่
main— OS เรียกมันให้ intข้างหน้า =mainคืนค่าเป็นจำนวนเต็ม ให้ OSvoidในวงเล็บ = ไม่รับ argument (แบบรับ argument คือint main(int argc, char *argv[])— บทที่ 24){ }ครอบ ตัวฟังก์ชัน (function body)
return จาก main
return 0;ค่าที่ main คืนคือ exit status ของโปรแกรม:
0= สำเร็จ- ไม่ใช่ 0 = มีข้อผิดพลาด (โปรแกรม CLI ใช้เลขต่างกันบอกชนิด error)
เช็คใน shell ได้:
./hello
echo $? # พิมพ์ exit status ของคำสั่งล่าสุด<stdlib.h> มีค่าคงที่ EXIT_SUCCESS (0) และ EXIT_FAILURE (มักเป็น 1) ใช้แทนตัวเลขดิบได้
ถ้าไม่เขียน
returnในmain(เฉพาะmainเท่านั้น) C จะถือว่าreturn 0;ให้ — แต่เขียนให้ชัดดีกว่า
printf กับ format specifier
printf = “print formatted” สตริงแรกคือ format string ที่มี placeholder ขึ้นต้นด้วย % — ค่าจริงตามหลังมาตามลำดับ
#include <stdio.h>
int main(void)
{
int count = 5;
double price = 19.99;
char grade = 'A';
printf("จำนวน: %d\n", count);
printf("ราคา: %.2f บาท\n", price);
printf("เกรด: %c\n", grade);
printf("%d ชิ้น ราคารวม %.2f\n", count, price * count);
return 0;
}จำนวน: 5
ราคา: 19.99 บาท
เกรด: A
5 ชิ้น ราคารวม 99.95specifier ที่ใช้บ่อย
| specifier | ใช้กับ | ตัวอย่างผล |
|---|---|---|
%d | int | 42 |
%u | unsigned int | 42 |
%ld | long | 100000 |
%f | float / double | 3.140000 |
%.2f | ทศนิยม 2 ตำแหน่ง | 3.14 |
%c | char (ตัวเดียว) | A |
%s | สตริง (char *) | hello |
%x | เลขฐาน 16 | 2a |
%p | pointer (ที่อยู่) | 0x7ffe... |
%% | เครื่องหมาย % ตรง ๆ | % |
ปรับความกว้าง/การจัดชิด:
printf("%5d\n", 42); // " 42" (กว้าง 5 ชิดขวา)
printf("%-5d|\n", 42); // "42 |" (ชิดซ้าย)
printf("%05d\n", 42); // "00042" (เติม 0)
printf("%8.2f\n", 3.14); // " 3.14"จับคู่ specifier กับชนิดให้ถูก
นี่คือกับดักใหญ่ของ C — printf ไม่ตรวจ ว่า specifier ตรงกับชนิดของค่าไหม ผิดแล้วได้ค่าขยะหรือโปรแกรมพัง:
double x = 3.14;
printf("%d\n", x); // ❌ %d คาดหวัง int แต่ให้ double — undefined behavior (ไม่ใช่แค่ค่าเพี้ยน)
printf("%f\n", x); // ✅ ถูก-Wall จับกรณีนี้ได้: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double'
putchar และ puts
พิมพ์แบบง่ายเมื่อไม่ต้อง format:
puts("บรรทัดนี้มี \\n ให้อัตโนมัติ"); // เติม \n ท้ายให้เอง
putchar('X'); // พิมพ์ตัวอักษรตัวเดียว
putchar('\n');เรื่องที่คนมักพลาด
- specifier ไม่ตรงชนิด —
%dกับdouble,%fกับint,%sกับint— ผลลัพธ์เพี้ยนหรือ crash เปิด-Wallให้จับ - ลืม
\n— output ติดกันหมด หรือไม่โผล่จนกว่าโปรแกรมจบ (เพราะ buffering) %sกับตัวแปรที่ไม่ใช่สตริงที่ปิดด้วย'\0'—printfอ่านต่อไปเรื่อย ๆ จนเจอ 0 หรือ segfault- จำนวน argument ไม่ตรงกับ
%—printf("%d %d\n", 5)อ่าน argument ตัวที่สองจากขยะบน stack - ใช้
//คอมเมนต์ในโค้ดที่ต้อง compile ด้วย-std=c89—//เพิ่มมาใน C99 ถ้าใช้ c17 ไม่มีปัญหา 'A'(single quote) กับ"A"(double quote) ต่างกัน —'A'เป็น character constant ชนิดintใน C (ค่า 65) เก็บลงcharได้;"A"คือสตริง 2 ไบต์ ('A'+'\0')
สรุป
#include <header.h>ดึงการประกาศฟังก์ชันเข้ามา (<>ระบบ,""ของเรา)int main(void) { ... return 0; }— จุดเริ่ม,return= exit status (0 = สำเร็จ)printf("format", args...)—%dint,%f/.2fdouble,%cchar,%sstring,%ppointer- specifier ต้องตรงชนิดของค่าเป๊ะ —
printfไม่ตรวจให้ ใช้-Wallช่วย puts()/putchar()สำหรับพิมพ์ง่าย ๆ
บทถัดไปว่าด้วยตัวแปรและชนิดข้อมูลพื้นฐาน — int, char, float, double, sizeof และการรับค่าจากผู้ใช้