1.1 ตัวแปร
โปรแกรม Hello World พิมพ์ข้อความเดิมทุกครั้งที่รัน แต่โจทย์จริงแทบทุกข้อให้ตัวเลขมา แล้วให้เราคำนวณต่อ เช่น รับคะแนนสอบมาแล้วหาผลรวม รับความกว้างกับความยาวมาแล้วหาพื้นที่ โปรแกรมจึงต้องมีที่จำตัวเลขไว้ใช้ ที่จำนั้นเรียกว่า ตัวแปร (variable)
ตัวแปรคือกล่องที่มีชื่อ
ให้นึกถึงกล่องในหน่วยความจำของคอมพิวเตอร์ แต่ละกล่องมีสามอย่าง
- ชื่อ ใช้เรียกกล่องนั้น เช่น
score - ค่า ของที่เก็บอยู่ในกล่อง เก็บได้ทีละหนึ่งค่า ใส่ค่าใหม่เมื่อไร ค่าเก่าหายทันที
- ชนิด (type) บอกว่ากล่องนี้เก็บของแบบไหน เช่น
intเก็บจำนวนเต็ม
ตัวแปรตัวแรก
#include <stdio.h>
int main()
{
int score; // (1)!
score = 85; // (2)!
printf("score = %d\n", score); // (3)!
return 0;
}
- ประกาศ (declare) ตัวแปร บอก compiler ว่าขอกล่องชนิด
intชื่อscoreหนึ่งกล่องintย่อมาจาก integer แปลว่าจำนวนเต็ม - กำหนดค่า (assign) เอา 85 ใส่ลงกล่อง
score %dคือช่องว่างที่รอตัวเลขprintfจะเอาค่าของscoreมาแทนที่ตรงนั้น
score = 85
ข้อความ score = ในเครื่องหมายคำพูดพิมพ์ออกมาตรงตัว ส่วน %d ถูกแทนด้วย 85 ตัว d มาจาก decimal คือเลขฐานสิบ
ไม่แสดงบรรทัด Process returned แล้ว
ตั้งแต่บทนี้ไป กล่องผลลัพธ์จะแสดงเฉพาะสิ่งที่โปรแกรมพิมพ์ ส่วนสองบรรทัดท้ายที่ Code::Blocks เติมให้ น้องจะยังเห็นในเครื่องตัวเองเหมือนเดิม
ประกาศพร้อมกำหนดค่า
ประกาศแล้วกำหนดค่าในบรรทัดเดียวได้ เขียนสั้นกว่า และกันลืมใส่ค่า
#include <stdio.h>
int main()
{
int math = 78;
int science = 91;
printf("math = %d, science = %d\n", math, science);
printf("total = %d\n", math + science);
return 0;
}
math = 78, science = 91
total = 169
printfหนึ่งครั้งมี%dกี่ตัวก็ได้ ค่าที่อยู่หลังข้อความจะถูกเติมลงไป ตามลำดับ ตัวแรกเติม%dตัวแรก ตัวที่สองเติมตัวที่สอง- ค่าที่เติมไม่จำเป็นต้องเป็นตัวแปรเดี่ยวๆ จะเป็นผลคำนวณอย่าง
math + scienceก็ได้
ตัวแปรชนิดเดียวกันประกาศในบรรทัดเดียว คั่นด้วยจุลภาคได้ แบบนี้
int math = 78, science = 91;
เปลี่ยนค่าตัวแปร
ชื่อว่าตัวแปรเพราะค่าเปลี่ยนได้ โปรแกรมทำงานจากบนลงล่างทีละบรรทัด ตอนที่ printf ทำงาน ตัวแปรมีค่าอะไร ก็พิมพ์ค่านั้น
#include <stdio.h>
int main()
{
int score = 85;
printf("first = %d\n", score);
score = 92;
printf("second = %d\n", score);
score = score + 5;
printf("with bonus = %d\n", score);
return 0;
}
first = 85
second = 92
with bonus = 97
บรรทัดที่ 9 score = score + 5; ดูแปลกถ้าคิดแบบคณิตศาสตร์ เพราะไม่มีเลขไหนเท่ากับตัวมันเองบวก 5 แต่ในภาษา C เครื่องหมาย = ไม่ได้แปลว่าเท่ากับ แปลว่า "เอาค่าทางขวาไปใส่ในกล่องทางซ้าย" คอมพิวเตอร์ทำสองจังหวะ
- คำนวณด้านขวาก่อน ตอนนี้
scoreเป็น 92 ได้ 92 + 5 = 97 - เอา 97 ใส่ลงกล่อง
scoreทับค่าเดิม
รูปแบบนี้จะเจออีกบ่อยมาก เช่น บวกคะแนนสะสม นับจำนวนครั้ง ตอนเรียนเรื่องวนซ้ำในบทที่ 4
ตั้งชื่อตัวแปร
กฎที่ compiler บังคับ
- ใช้ได้แค่ตัวอักษรภาษาอังกฤษ ตัวเลข และขีดล่าง
_ห้ามมีช่องว่าง - ห้ามขึ้นต้นด้วยตัวเลข
- ห้ามซ้ำกับคำสงวนของภาษา C เช่น
intreturndouble - ตัวพิมพ์เล็กกับพิมพ์ใหญ่ถือว่าต่างกัน
scoreกับScoreเป็นคนละตัว
| ชื่อ | ใช้ได้ไหม |
|---|---|
score | ได้ |
total_score | ได้ คั่นคำด้วยขีดล่าง อ่านง่าย |
score2 | ได้ ตัวเลขอยู่หลังได้ |
2nd_score | ไม่ได้ ขึ้นต้นด้วยตัวเลข |
total score | ไม่ได้ มีช่องว่าง |
return | ไม่ได้ เป็นคำสงวน |
นอกจากกฎของ compiler ให้ตั้งชื่อที่บอกว่าเก็บอะไร width กับ height อ่านแล้วรู้เรื่องทันที แต่ a กับ b ต้องย้อนไปดูว่าคืออะไร เอกสารนี้ใช้ภาษาอังกฤษตัวเล็ก คั่นคำด้วยขีดล่าง
รับค่าจากผู้ใช้ด้วย scanf
ถ้าค่าตัวแปรเขียนตายตัวในโค้ด โปรแกรมก็ตอบได้คำตอบเดียว โจทย์ในการแข่งขันจะส่ง ข้อมูลเข้า (input) มาให้ แล้วให้โปรแกรมคำนวณ คำสั่งที่รับตัวเลขเข้ามาใส่ตัวแปรคือ scanf
#include <stdio.h>
int main()
{
int age;
scanf("%d", &age);
printf("next year you will be %d\n", age + 1);
return 0;
}
15
next year you will be 16
กด F9 แล้วหน้าต่างดำจะว่างอยู่ เพราะโปรแกรมรอให้พิมพ์ พิมพ์ 15 แล้วกด Enter โปรแกรมถึงจะทำงานต่อ ในหน้าต่างจริงจะเห็นเลขที่เราพิมพ์อยู่บรรทัดบน ส่วนในเอกสารนี้แยกข้อมูลเข้ากับผลลัพธ์ไว้คนละกล่อง
"%d"บอกscanfว่าจะอ่านจำนวนเต็มหนึ่งตัว&ageคือ ที่อยู่ ของกล่องageในหน่วยความจำscanfต้องรู้ว่ากล่องอยู่ที่ไหนถึงจะเอาค่าไปใส่ได้ ตอนนี้จำไว้ว่า ตัวแปรใน scanf ต้องมี&นำหน้า ส่วนprintfไม่ต้องมี เรื่องที่อยู่จะอธิบายเต็มๆ ในบทที่ 10
รับหลายค่าพร้อมกันก็ได้ ใส่ %d ให้ครบตามจำนวนตัวแปร
#include <stdio.h>
int main()
{
int width, height;
scanf("%d %d", &width, &height);
printf("area = %d\n", width * height);
return 0;
}
12 7
area = 84
เครื่องหมาย * คือคูณ หน้าถัดไปจะดูเครื่องหมายคำนวณทั้งหมด ข้อมูลเข้าสองตัวจะคั่นด้วยช่องว่างหรือขึ้นบรรทัดใหม่ก็ได้ scanf อ่านได้ทั้งสองแบบ รายละเอียดอื่นของ scanf อยู่ในบทที่ 2
ลองทำให้พัง
ทำทีละแบบ อ่านข้อความที่ได้ แล้วแก้กลับให้ถูกก่อนลองแบบถัดไป
แบบที่ 1 พิมพ์ชื่อตัวแปรผิด
ในโปรแกรม score แก้บรรทัด printf ให้ใช้ scroe แทน score
score.c:7:28: error: 'scroe' undeclared (first use in this function); did you mean 'score'?
7 | printf("score = %d\n", scroe);
| ^~~~~
| score
score.c:7:28: note: each undeclared identifier is reported only once for each function it appears in
score.c:5:9: warning: variable 'score' set but not used [-Wunused-but-set-variable]
5 | int score;
| ^~~~~
undeclared แปลว่ายังไม่ได้ประกาศ compiler ไม่รู้จักชื่อ scroe เพราะกล่องที่เราขอไว้ชื่อ score และมันเดาให้ด้วยว่าน่าจะหมายถึง score ถ้าลืมประกาศตัวแปรก่อนใช้ ก็ได้ error แบบเดียวกันนี้
บรรทัด note บอกแค่ว่าชื่อที่ไม่รู้จักชื่อหนึ่งจะถูกฟ้องครั้งเดียวต่อฟังก์ชัน ไม่ต้องแก้อะไร ส่วน warning set but not used ขึ้นตามมาเพราะตอนนี้ score ถูกใส่ค่าแต่ไม่มีใครเอาไปใช้ พอแก้ชื่อให้ถูก ทั้งสองบรรทัดก็หายไปพร้อม error
แบบที่ 2 ตั้งชื่อขึ้นต้นด้วยตัวเลข
ในโปรแกรม score เปลี่ยนบรรทัด int score; เป็น
int 2nd_score = 90;
score.c:5:9: error: invalid suffix "nd_score" on integer constant
5 | int 2nd_score = 90;
| ^~~~~~~~~
score.c:5:9: error: expected identifier or '(' before numeric constant
score.c:6:5: error: 'score' undeclared (first use in this function)
6 | score = 85;
| ^~~~~
score.c:6:5: note: each undeclared identifier is reported only once for each function it appears in
compiler เห็นเลข 2 แล้วคิดว่าเป็นตัวเลข ตามด้วยตัวอักษรแปลกๆ nd_score ข้อความไม่ได้บอกตรงๆ ว่าตั้งชื่อผิดกฎ แต่ชี้ตำแหน่งให้ ถ้าเจอ error ที่อ่านไม่เข้าใจ ให้ดูตำแหน่งที่ ^ ชี้ก่อนเสมอ
error บรรทัดที่สามเกิดตามมา เพราะประกาศพังไปแล้ว score จึงไม่มีอยู่จริง error ที่ต่อกันเป็นพรวนแบบนี้เจอบ่อย ให้แก้ตัวแรกก่อน แล้วคอมไพล์ใหม่ ตัวที่ตามมามักหายไปเอง
แบบที่ 3 ใช้ตัวแปรที่ยังไม่ได้ใส่ค่า
ลบบรรทัด score = 85; ออก เหลือแค่ประกาศแล้วพิมพ์เลย
score.c:6:5: warning: 'score' is used uninitialized [-Wuninitialized]
6 | printf("score = %d\n", score);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
score.c:5:9: note: 'score' was declared here
5 | int score;
| ^~~~~
คราวนี้เป็น warning (คำเตือน) ไม่ใช่ error โปรแกรมยังรันได้ แต่ค่าที่พิมพ์ออกมาคือของที่ค้างอยู่ในหน่วยความจำตรงนั้น เรียกว่า ค่าขยะ รันคนละเครื่องหรือคนละครั้งอาจได้ไม่เหมือนกัน คำเตือนนี้ขึ้นเพราะเราเปิด -Wall ไว้ตอนติดตั้ง ถ้าเห็น warning ให้แก้เหมือนเป็น error
แบบที่ 4 ลืม & ใน scanf
ในโปรแกรม age เขียน scanf("%d", age); โดยไม่มี &
age.c:6:13: warning: format '%d' expects argument of type 'int *', but argument 2 has type 'int' [-Wformat=]
6 | scanf("%d", age);
| ~^ ~~~
| | |
| | int
| int *
age.c:6:5: warning: 'age' is used uninitialized [-Wuninitialized]
ยังเป็นแค่ warning จึงรันได้ แต่ scanf ไม่รู้ว่ากล่อง age อยู่ไหน เลขที่พิมพ์เข้าไปจึงไม่ถึงตัวแปร บนเครื่องที่เขียนเอกสารนี้ พิมพ์ 20 แล้วได้ next year you will be 1 ซึ่งไม่เกี่ยวกับ 20 เลย ส่วน int * ในข้อความคือชนิดของที่อยู่ จะได้เรียนในบทที่ 10
สรุป
- ตัวแปรคือกล่องที่มีชื่อ ชนิด และค่า ต้องประกาศก่อนใช้ เช่น
int score; =คือเอาค่าทางขวาใส่กล่องทางซ้าย ไม่ใช่เท่ากับprintf("%d", x)พิมพ์ค่าจำนวนเต็มscanf("%d", &x)รับค่าจำนวนเต็ม อย่าลืม&- ชื่อตัวแปรใช้ตัวอักษร ตัวเลข ขีดล่าง ห้ามขึ้นต้นด้วยตัวเลข และควรบอกว่าเก็บอะไร
- warning ไม่หยุดการคอมไพล์ แต่มักแปลว่าโปรแกรมจะได้ค่าผิด