← Lập trình C cơ bản

Bài 7 · Cơ bản · 19 phút· Cập nhật 11/06/2026

Toán tử & biểu thức

Biên soạn bởi Nguyễn Anh Tuấn

Làm việc với toán tử số học, so sánh, logic, phép gán, precedence; hiểu chia nguyên, %, bool trong C17/C23 và bẫy = vs ==.

Năm toán tử số học: + − * như toán học bình thường, nhưng /% có điều bất ngờ với số nguyên: / là chia LẤY PHẦN NGUYÊN (bỏ phần lẻ), còn % là lấy SỐ DƯ:

so-hoc.c

#include <stdio.h>

int main() {
    int a = 7, b = 2;
    printf("%d\n", a + b);   // 9
    printf("%d\n", a / b);   // 3  <- chia NGUYEN, khong phai 3.5!
    printf("%d\n", a % b);   // 1  <- so du
    return 0;
}

Kết quả khi chạy

9
3
1

Muốn ra 3.5?

Cho ít nhất một vế là số thực: 7.0 / 2 sẽ ra 3.5. Khi cả hai là int, C luôn chia nguyên. Vì sao chỉ một vế số thực là đủ đổi cả phép tính - bài Chuyển đổi kiểu dữ liệu và các cạm bẫy phổ biến sẽ giải thích tường tận.

Gõ một biểu thức số nguyên và xem C tính ra gì. Để ý /, %, và các phép so sánh cho ra 0 hoặc 1:

= 3

Tính như C trên số nguyên: / là chia nguyên (7/2 = 3), % là số dư; so sánh & logic cho ra 0 hoặc 1.

Các phép so sánh (< > <= >= == !=) trả về 1 nếu đúng, 0 nếu sai. Các phép logic (&& “và”, || “hoặc”, ! “không”) ghép nhiều điều kiện - với quy ước 0 là sai, khác 0 là đúng - chính những biểu thức này sẽ đứng trong ngoặc của if ở bài Rẽ nhánh: if/else & switch:

so-sanh.c

#include <stdio.h>

int main() {
    int x = 5;
    printf("%d\n", x > 3);            // 1  (dung)
    printf("%d\n", x == 10);          // 0  (sai)
    printf("%d\n", x > 0 && x < 10);  // 1  (ca hai dung)
    return 0;
}

Kết quả khi chạy

1
0
1
  • Đúng → 1, Sai → 0. (0 = sai, khác 0 = đúng.)
  • && cho 1 khi CẢ HAI vế đúng; || cho 1 khi CÓ vế đúng.
  • ! đảo: !0 = 1, !(khác 0) = 0.

C cũng có kiểu bool (boolean) cho biến chỉ mang giá trị đúng/sai - với C17 mà khoá này dùng, cần thêm #include <stdbool.h>:

bool.c - biên dịch với -std=c17

#include <stdio.h>
#include <stdbool.h>   /* C99-C17: bool, true, false nam trong header nay */

int main() {
    bool dang_hoc = true;
    bool troi_mua = false;

    printf("%d\n", dang_hoc);   // 1
    printf("%d\n", troi_mua);   // 0
    printf("%d\n", 5 > 3);      // 1 - so sanh van tra ve int 0/1
    return 0;
}

Kết quả khi chạy

1
0
1

Trung thực: bool có từ bao giờ?

Lịch sử ba chặng: trước C99 C chưa có boolean - dân C dùng int với quy ước 0/khác-0. C99 → C17 thêm kiểu _Bool, còn tên đẹp bool / true / false phải lấy từ <stdbool.h> - quên include là lỗi biên dịch. Từ C23 chúng thành từ khoá, không cần header nữa. Và lưu ý: phép so sánh vẫn trả về int 0/1 (kể cả C23) - nên quy ước 0/1 ở trên vẫn là nền móng.

Như toán học, * / % làm trước + −; số học làm trước so sánh; so sánh trước logic. Khi nghi ngờ, dùng dấu ngoặc cho rõ:

uu-tien.c

#include <stdio.h>

int main() {
    printf("%d\n", 2 + 3 * 4);    // 14  (nhan truoc)
    printf("%d\n", (2 + 3) * 4);  // 20  (ngoac truoc)
    printf("%d\n", 2 + 3 > 4);    // 1   (5 > 4)
    return 0;
}

Kết quả khi chạy

14
20
1
  • Nhân/chia/dư trước cộng/trừ; số học trước so sánh; so sánh trước && và ||.
  • Ngoặc ( ) luôn được tính trước - dùng để vừa đúng vừa dễ đọc.

Có cách viết tắt khi cập nhật một biến: +=, −=, *=…, và ++ / −− để tăng/giảm 1:

gan.c

#include <stdio.h>

int main() {
    int n = 5;
    n += 3;   // n = n + 3  ->  8
    n++;      // n = n + 1  ->  9
    printf("%d\n", n);   // 9
    return 0;
}

Kết quả khi chạy

9

Bẫy kinh điển: “=” không phải “==”

if (diem == 10) ← SO SÁNH, đúng.
if (diem = 10) ← GÁN diem = 10 rồi luôn “đúng” - sai logic mà không báo lỗi! Nhớ: so sánh bằng thì dùng hai dấu bằng.

Câu hỏi thường gặp

Vì cả 7 và 2 đều là số nguyên (int) nên C làm “chia nguyên”: lấy phần nguyên, bỏ phần lẻ. Muốn 3.5 thì ít nhất một vế phải là số thực, vd 7.0 / 2 hoặc (float)7 / 2.

Là số dư của phép chia nguyên: 7 % 2 = 1, 10 % 5 = 0. Rất hay dùng để kiểm tra chia hết / chẵn lẻ: n % 2 == 0 nghĩa là n chẵn.

Là số nguyên int: 1 nếu ĐÚNG, 0 nếu SAI - kể cả khi bạn dùng kiểu bool (mọi phiên bản C, gồm cả C23). Quy ước nền của C: số 0 coi là “sai”, mọi số khác 0 coi là “đúng”. Còn kiểu bool thì có từ C99: với C17 cần #include <stdbool.h>; từ C23 bool/true/false là từ khoá, không cần header.

“=” là phép GÁN (đặt giá trị vào biến). “==” là phép SO SÁNH BẰNG. Viết nhầm if(x = 5) thực ra là gán 5 cho x rồi luôn đúng - đây là bẫy kinh điển của người mới.

Không. “&&” là “VÀ” logic (dùng cho điều kiện). “&” là phép trên từng bit (bitwise) - học sau, ở khoá nâng cao. Người mới hầu như luôn dùng &&.

Cả hai đều tăng n lên 1. Khác nhau chỉ khi dùng GIỮA một biểu thức lớn (n++ trả giá trị cũ rồi mới tăng; ++n tăng trước rồi trả). Lúc mới học, hãy để chúng đứng riêng một dòng cho an toàn.

Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.

Tick những điều em tự tin làm được sau khi học bài này. 0/6

Trả lời vài câu để chắc rằng em đã nắm bài.

Câu 1/3 Điểm: 0

Trong C, 7 / 2 (cả hai là số nguyên) cho kết quả là bao nhiêu?

  1. 1

    Chia và dư

    Dùng công cụ ở Bước 2: tính 17 / 517 % 5.

    Hoàn thành khi: 17 / 5 = 3 (chia nguyên), 17 % 5 = 2 (số dư).

  2. 2

    Kiểm tra chẵn

    Viết một biểu thức kiểm tra số 8 có phải số chẵn không.

    Hoàn thành khi: 8 % 2 == 0 → cho ra 1 (đúng = chẵn).

  3. 3

    Lấy kết quả 3.5

    Phải sửa 7 / 2 thế nào để kết quả ra 3.5?

    Hoàn thành khi: Cho một vế là số thực: 7.0 / 2 (hoặc 7 / 2.0). Khi đó là chia thực.

  4. 4

    Trong khoảng

    Với x = 5, viết biểu thức kiểm tra “x nằm trong khoảng 1 đến 10”.

    Hoàn thành khi: x >= 1 && x <= 10 → 1. (Thử x = 5 trong công cụ.)

  5. 5

    Bẫy “=” và “==”

    Đoạn if (diem = 10) luôn chạy vào nhánh trong dù điểm khác 10. Vì sao? Sửa lại.

    Hoàn thành khi: “=” là gán: diem được đặt = 10 và kết quả khác 0 nên luôn đúng. Sửa thành if (diem == 10).

  6. 6

    Thứ tự ưu tiên

    2 + 3 * 4 - 1 bằng mấy? Giải thích thứ tự.

    Hoàn thành khi: 13 - nhân trước (3*4=12), rồi 2 + 12 − 1 = 13.