[0]zero-index clytie0x/zero-index

1.2 ตัวดำเนินการและการคำนวณ

มีตัวแปรแล้ว ต่อไปคือคำนวณ เครื่องหมายบวก ลบ คูณ หาร เรียกรวมว่า ตัวดำเนินการ (operator) ส่วนข้อความที่เอาตัวแปร ตัวเลข และตัวดำเนินการมาต่อกัน เช่น width * height เรียกว่า นิพจน์ (expression) หน้านี้มีสองเรื่องที่ต่างจากคณิตศาสตร์ในห้องเรียน คือการหารจำนวนเต็ม และเครื่องหมาย % สองเรื่องนี้ใช้ในโจทย์แข่งขันบ่อยมาก

ห้าตัวดำเนินการพื้นฐาน

#include <stdio.h>

int main()
{
    int a = 17, b = 5;
    printf("%d + %d = %d\n", a, b, a + b);
    printf("%d - %d = %d\n", a, b, a - b);
    printf("%d * %d = %d\n", a, b, a * b);
    printf("%d / %d = %d\n", a, b, a / b);
    printf("%d %% %d = %d\n", a, b, a % b);
    return 0;
}
17 + 5 = 22
17 - 5 = 12
17 * 5 = 85
17 / 5 = 3
17 % 5 = 2
ตัวดำเนินการความหมาย17 กับ 5
+บวก22
-ลบ12
*คูณ ใช้ดอกจัน ไม่ใช่ตัว x85
/หาร ถ้าทั้งสองข้างเป็นจำนวนเต็ม จะได้แค่ส่วนที่เป็นจำนวนเต็ม3
%เศษจากการหาร อ่านว่า mod2

สังเกตบรรทัดที่ 10 ใน printf เครื่องหมาย % มีหน้าที่พิเศษอยู่แล้ว (เช่น %d) ถ้าอยากให้พิมพ์ % ออกมาเฉยๆ ต้องเขียนสองตัวเป็น %%

หารจำนวนเต็ม กับเศษ

17 หารด้วย 5 ในชีวิตจริงได้ 3.4 แต่ในภาษา C ถ้าทั้งสองข้างเป็น int ผลก็เป็น int ส่วนทศนิยมจะถูก ตัดทิ้ง ไม่ปัดขึ้น ได้ 3 ส่วนที่หายไปหาคืนได้ด้วย %

17 = 5 * 3 + 2
17 / 5 = 3    (ได้กี่รอบ)
17 % 5 = 2    (เหลือเศษเท่าไร)

ถ้านึกไม่ออก ให้นึกถึงลูกอม 17 เม็ดแบ่งให้เพื่อน 5 คน ได้คนละ 3 เม็ด เหลือ 2 เม็ด / คือได้คนละเท่าไร % คือเหลือเท่าไร

สองตัวนี้ใช้คู่กันบ่อยมาก ตัวอย่างที่จะเจอซ้ำๆ

  • n % 2 ได้ 0 ถ้า n เป็นเลขคู่ ได้ 1 ถ้าเป็นเลขคี่
  • n % 10 คือหลักหน่วยของ n
  • n / 10 คือ n ที่ตัดหลักหน่วยทิ้ง
  • n / 60 กับ n % 60 แปลงนาทีเป็นชั่วโมงกับนาที

ถ้าเป็นเลขติดลบ

#include <stdio.h>

int main()
{
    int a = -17, b = 5;
    printf("%d %d\n", a / b, a % b);
    printf("%d %d\n", 17 / -5, 17 % -5);
    return 0;
}
-3 -2
-3 2

ภาษา C ตัดทศนิยมทิ้งไปทาง 0 เสมอ -3.4 จึงกลายเป็น -3 ไม่ใช่ -4 และเศษจาก % มีเครื่องหมายตามตัวตั้ง ตอนนี้ยังไม่ต้องจำ แค่รู้ไว้ว่าเลขติดลบกับ % ต้องระวัง เมื่อไรเจอโจทย์ที่มีเลขติดลบ ให้ลองรันดูก่อนเชื่อ

ลำดับการคำนวณ

เหมือนคณิตศาสตร์ คูณหารทำก่อนบวกลบ

  • * / % ทำก่อน
  • + - ทำทีหลัง
  • ถ้าลำดับเท่ากัน ทำจากซ้ายไปขวา
  • อยากให้อะไรทำก่อน ใส่วงเล็บ
#include <stdio.h>

int main()
{
    printf("%d\n", 2 + 3 * 4);
    printf("%d\n", (2 + 3) * 4);
    printf("%d\n", 20 / 2 * 5);
    printf("%d\n", 20 / (2 * 5));
    printf("%d\n", 7 + 10 % 4);
    return 0;
}
14
20
50
2
9

บรรทัดที่ 7 ทำ 20 / 2 ได้ 10 ก่อน แล้วค่อยคูณ 5 เพราะลำดับเท่ากันเลยทำจากซ้ายไปขวา บรรทัดที่ 9 % อยู่ระดับเดียวกับคูณหาร จึงทำ 10 % 4 ได้ 2 ก่อนบวก 7

ไม่แน่ใจ ใส่วงเล็บ

วงเล็บเกินไม่ทำให้โปรแกรมช้าลง และทำให้คนอ่านไม่ต้องนั่งนึกลำดับ (a * b) + c อ่านง่ายกว่าให้คนอ่านไปจำกฎเอง

ตัวย่อ += และ ++

การเอาค่าเดิมของตัวแปรมาคำนวณแล้วเก็บกลับที่เดิม อย่าง coins = coins + 5; เกิดบ่อยจนภาษา C มีตัวย่อให้

เขียนย่อเหมือนกับ
x += 5;x = x + 5;
x -= 5;x = x - 5;
x *= 5;x = x * 5;
x /= 5;x = x / 5;
x %= 5;x = x % 5;
x++;x = x + 1;
x--;x = x - 1;
#include <stdio.h>

int main()
{
    int coins = 10;
    coins += 5;
    printf("%d\n", coins);
    coins -= 3;
    printf("%d\n", coins);
    coins *= 2;
    printf("%d\n", coins);
    coins++;
    printf("%d\n", coins);
    coins--;
    printf("%d\n", coins);
    return 0;
}
15
12
24
25
24

++ กับ -- จะได้ใช้เยอะมากตอนเรียนวนซ้ำในบทที่ 4 ตอนนี้ให้ใช้เป็นคำสั่งเดี่ยวๆ ในบรรทัดของมันเองแบบในตัวอย่าง อย่าเอาไปผสมในนิพจน์อื่น เหตุผลอยู่ในส่วนลองทำให้พังท้ายหน้า

แปลงนาทีเป็นชั่วโมง

หนังเรื่องหนึ่งยาว 135 นาที คิดเป็นกี่ชั่วโมงกี่นาที หนึ่งชั่วโมงมี 60 นาที จำนวนชั่วโมงคือ 135 หารด้วย 60 แบบตัดเศษ ส่วนนาทีที่เหลือคือเศษ

#include <stdio.h>

int main()
{
    int total_minutes;
    scanf("%d", &total_minutes);
    int hours = total_minutes / 60;
    int minutes = total_minutes % 60;
    printf("%d hours %d minutes\n", hours, minutes);
    return 0;
}
135
2 hours 15 minutes

ประกาศตัวแปรตรงกลางโปรแกรมได้ ไม่จำเป็นต้องอยู่บรรทัดแรกของ main ประกาศตรงที่เริ่มใช้จะอ่านง่ายกว่า

วิธีเดียวกันใช้กับหน่วยอื่นได้ทุกแบบ เช่น วินาทีเป็นนาที เซนติเมตรเป็นเมตร วันเป็นสัปดาห์ ลองเปลี่ยนโปรแกรมนี้ให้แปลงวันเป็นกี่สัปดาห์กี่วันดู

แยกหลักของตัวเลข

โจทย์แนว "หาผลรวมของเลขโดด" หรือ "กลับหลักตัวเลข" ต้องแยกตัวเลขออกเป็นหลักๆ ก่อน ใช้ / กับ % ด้วย 10 ตามตารางนี้ ถ้า n คือ 472

นิพจน์ได้ความหมาย
n % 102หลักหน่วย
n / 1047ตัดหลักหน่วยทิ้ง
n / 10 % 107หลักสิบ ตัดหลักหน่วยทิ้งก่อน แล้วเอาหลักขวาสุด
n / 1004หลักร้อย
#include <stdio.h>

int main()
{
    int n;
    scanf("%d", &n);
    int hundreds = n / 100;
    int tens = n / 10 % 10;
    int ones = n % 10;
    printf("%d %d %d\n", hundreds, tens, ones);
    printf("sum = %d\n", hundreds + tens + ones);
    return 0;
}
472
4 7 2
sum = 13

จำหลักไว้สองข้อ % 10 หยิบหลักขวาสุด / 10 ตัดหลักขวาสุดทิ้ง ตอนนี้โปรแกรมทำได้แค่เลขที่รู้จำนวนหลักล่วงหน้า พอเรียนวนซ้ำในบทที่ 4 จะใช้สองข้อนี้ซ้ำไปเรื่อยๆ จนแยกเลขกี่หลักก็ได้

ลองทำให้พัง

แบบที่ 1 ลืมวงเล็บ

หาค่าเฉลี่ยของคะแนนสองวิชา 80 กับ 90 คำตอบที่ถูกคือ 85

#include <stdio.h>

int main()
{
    int math = 80, science = 90;
    printf("%d\n", math + science / 2);
    return 0;
}
125

compiler ไม่เตือนอะไรเลย เพราะโค้ดถูกไวยากรณ์ทุกอย่าง แค่ไม่ใช่สิ่งที่เราตั้งใจ หารทำก่อนบวก จึงได้ 80 + 45 = 125 แก้เป็น (math + science) / 2 จะได้ 85 ความผิดพลาดแบบนี้อันตรายกว่า error เพราะไม่มีอะไรบอก ต้องลองคิดคำตอบด้วยมือแล้วเทียบเอง

แบบที่ 2 หารด้วยศูนย์

โปรแกรมแบ่งลูกอม รับจำนวนลูกอมกับจำนวนคน แล้วพิมพ์ว่าได้คนละกี่เม็ด ส่วนสำคัญคือสองบรรทัดนี้

scanf("%d %d", &total, &people);
printf("%d\n", total / people);

คอมไพล์ผ่านโดยไม่มีคำเตือน เพราะตอนคอมไพล์ยังไม่รู้ว่า people จะเป็น 0 แต่พอรันแล้วป้อน 10 0 โปรแกรมปิดตัวทันทีโดยไม่พิมพ์อะไร Code::Blocks ขึ้นว่า

Process returned -1073741676 (0xC0000094)

ปกติเลขหลัง Process returned คือ 0 ถ้าเห็นเลขติดลบแปลกๆ แปลว่าโปรแกรมพังกลางทาง 0xC0000094 คือรหัสของ Windows ที่แปลว่าหารจำนวนเต็มด้วยศูนย์ ถ้าส่งโปรแกรมแบบนี้ให้ grader ข้อนั้นจะได้ผลว่า runtime error ใช้ % ด้วย 0 ก็พังแบบเดียวกัน

แบบที่ 3 ใช้ ++ กับตัวแปรเดียวกันซ้ำ

int count = 5;
count = count++;
count.c:6:11: warning: operation on 'count' may be undefined [-Wsequence-point]
    6 |     count = count++;
      |     ~~~~~~^~~~~~~~~

บรรทัดนี้ทั้งกำหนดค่าให้ count และเพิ่มค่า count ในคำสั่งเดียว มาตรฐานภาษา C ไม่ได้กำหนดว่าอะไรต้องเกิดก่อน (undefined แปลว่าไม่ได้กำหนด) compiler แต่ละตัวจึงให้ผลต่างกันได้ บนเครื่องที่เขียนเอกสารนี้พิมพ์ count ออกมาได้ 5 ไม่ใช่ 6 อย่างที่หลายคนคาด ถ้าต้องการเพิ่มค่าหนึ่ง เขียน count++; อย่างเดียวพอ

สรุป

  • + - * / % คือบวก ลบ คูณ หาร เศษ
  • int / int ตัดทศนิยมทิ้ง % ให้เศษที่ถูกตัด
  • % 10 หยิบหลักขวาสุด / 10 ตัดหลักขวาสุดทิ้ง
  • คูณหารก่อนบวกลบ ลำดับเท่ากันทำจากซ้ายไปขวา ไม่แน่ใจใส่วงเล็บ
  • x += 5 คือ x = x + 5 และ x++ คือเพิ่มหนึ่ง ใช้เป็นคำสั่งเดี่ยวๆ
  • หารหรือ mod ด้วย 0 ทำให้โปรแกรมพังตอนรัน