Nhập môn Tư duy Lập trình/Biến, phép gán, và kiểu — dấu bằng không phải 'bằng nhau'
3/7
Bài 3 / 7~13 phútMáy thực thi lệnh thế nàoMiễn phí lượt xem

Biến, phép gán, và kiểu — dấu bằng không phải 'bằng nhau'

x = x + 1 vô lý trong toán nhưng hợp lệ ở đây: dấu bằng không so sánh mà chép giá trị vào một ô nhớ có tên, sau khi vế phải đã tính xong.

TL;DR: Dấu bằng trong code là phép gán, không phải phép so sánh "bằng nhau" như trong toán học: máy tính xong toàn bộ vế phải trước, rồi mới chép kết quả vào ô nhớ bên trái. Mỗi ô nhớ chỉ giữ đúng một giá trị tại một thời điểm, nên gán lại là ghi đè: giá trị cũ mất hẳn, không có cách nào lấy lại nếu chưa được cất sang một ô khác. Dòng lệnh x = x + 1 vô nghiệm nếu coi là phương trình toán học, nhưng lại hoàn toàn hợp lệ trong code vì thứ tự tính toán khác hẳn: vế phải dùng giá trị CŨ của x, còn vế trái nhận giá trị MỚI, hai thời điểm khác nhau nên không hề mâu thuẫn.

Ở bài trước bạn đã biết máy thi hành đúng từng dòng lệnh theo thứ tự, không suy diễn ý định người viết. Giờ thử đọc một dòng lệnh cụ thể:

x = x + 1

Nếu đây là một phương trình toán học, nó vô nghiệm — không có số nào cộng thêm 1 mà vẫn bằng chính nó. Nhưng dòng này chạy được, không báo lỗi gì cả. Nhiều tài liệu nói "biến lưu giá trị" rồi dừng ở đó, khiến người học đoán mò cơ chế. Bài này giải thích đúng cơ chế phép gán để bạn đọc được bất kỳ dòng gán nào, kể cả dòng "trông vô lý" như trên.

1. Analogy — tủ ký gửi có dán nhãn tên

Cứ hình dung dãy tủ ký gửi ở một siêu thị. Mỗi tủ có một nhãn tên dán cố định ở cửa, chẳng hạn tủ dán nhãn diem. Bên trong tủ chỉ chứa đúng một món đồ tại một thời điểm: cất món mới nghĩa là mở tủ, lấy món cũ ra (nếu có), rồi đặt món mới vào — món cũ không được giữ lại đâu cả, trừ khi bạn chủ động mang nó sang tủ khác trước. Ô nhớ trong chương trình hoạt động y hệt: một chỗ chứa có nhãn tên cố định, nhưng giá trị bên trong đổi được, và mỗi lần đổi là một lần ghi đè hoàn toàn.

Đời thườngÔ nhớ trong chương trình
Tủ ký gửi dán nhãn diemÔ nhớ có tên diem (biến)
Đặt món đồ mới vào tủ, món cũ bị lấy raGán giá trị mới, giá trị cũ bị ghi đè và mất
Nhãn dán ngoài tủ không đổi dù bên trong đổi mónTên biến giữ nguyên xuyên suốt chương trình, chỉ giá trị bên trong thay đổi
Một tủ chỉ chứa đúng MỘT món tại một thời điểmMột ô nhớ chỉ giữ đúng MỘT giá trị tại một thời điểm
Cách nhớ

Đọc x = 5 là "đem giá trị 5 gán vào ô x", không phải "x bằng 5". Đọc đúng cách, bạn sẽ không bao giờ bị x = x + 1 làm bối rối.

2. Phép gán — vế phải tính xong trước, rồi mới chép vào ô

Dòng gán đơn giản nhất chỉ có một giá trị có sẵn ở vế phải:

diem = 90

Máy làm đúng hai việc theo thứ tự: (1) xác định giá trị của vế phải — ở đây là 90, đã có sẵn, không cần tính gì; (2) chép giá trị đó vào ô có tên diem. Trước dòng này, ô diem có thể chưa tồn tại; sau dòng này, nó tồn tại và giữ giá trị 90.

Giờ đến phần thú vị: vế phải không phải một con số có sẵn, mà là một phép tính dùng lại chính tên ô bên trái.

x = 5
x = x + 1
print(x)
Thử đoán

Trước khi đọc tiếp: sau khi chạy xong ba dòng trên, dòng print(x) in ra số nào? Viết ra con số bạn tin chắc.

Output thật:

6

Đọc dòng x = x + 1 theo đúng thứ tự máy làm, không đọc theo thứ tự người: (1) máy nhìn vào ô x, thấy giá trị đang có là 5; (2) máy tính vế phải x + 1 bằng giá trị đó: 5 + 1 = 6 — một giá trị mới, tạm thời chưa gán vào đâu cả; (3) máy chép 6 vào ô x, ghi đè lên 5. Dòng print(x) chỉ đọc và in ra giá trị đang có trong ô x tại đúng thời điểm nó chạy, không tự nó làm thay đổi gì.

Sau dòng gán, ô x chỉ còn giữ 6. Giá trị 5 đã mất — đúng như tủ ký gửi ở phần 1: mở tủ, lấy món cũ ra, đặt món mới vào.

Đây là lý do x = x + 1 không mâu thuẫn với phương trình toán học cùng dạng. Trong toán, = khẳng định hai vế bằng nhau cùng một lúc. Trong code, =lệnh chạy theo thứ tự: "x" vế phải là giá trị , đọc trước khi gán; "x" vế trái nhận giá trị mới, chỉ tồn tại sau khi gán xong. Hai thời điểm khác nhau, nên không mâu thuẫn.

3. Cơ chế bên dưới — ba bước máy luôn làm

Ba bước ở mục 2 áp dụng cho mọi dòng gán, không riêng x = x + 1:

Ba hộp nối tiếp theo chiều ngang mô tả ba nhịp của một phép gán: đọc giá trị hiện tại trong ô nếu vế phải cần, tính toàn bộ vế phải ra một giá trị duy nhất, rồi chép giá trị đó vào ô bên trái và ghi đè giá trị cũ

Sơ đồ dưới đây vẽ đúng trạng thái ô nhớ trước và sau khi chạy x = x + 1, minh hoạ tại sao giá trị cũ và giá trị mới không bao giờ tồn tại cùng lúc trong ô x:

Ba khối cạnh nhau minh hoạ lệnh x = x + 1: khối trái là trạng thái TRƯỚC khi chạy với ô x giữ giá trị 5; khối giữa là dòng lệnh kèm ba nhịp đọc giá trị cũ, tính vế phải rồi ghi đè; khối phải là trạng thái SAU khi chạy với ô x giữ giá trị 6 — không nhịp nào cho thấy ô x giữ đồng thời cả hai giá trị

Trước dòng lệnh, ô x giữ 5. Ngay lúc máy tính xong vế phải nhưng chưa gán, ô x vẫn còn là 5: kết quả 6 đang "lơ lửng", chưa nằm trong ô nào. Chỉ sau bước gán, ô x mới đổi thành 65 bị ghi đè hẳn. Không có bước trung gian nào mà x giữ cả hai giá trị cùng lúc — đây là hệ quả trực tiếp của "một ô chỉ giữ một giá trị tại một thời điểm" đã nêu ở KeyTakeaways.

4. Trace một chuỗi ba lần gán liên tiếp

Cùng một cơ chế đó vẫn đúng khi có nhiều dòng gán nối tiếp, mỗi dòng dùng lại giá trị vừa ghi ở dòng trước:

# 1. Khoi tao diem xuat phat
diem = 10

# 2. Cong diem thuong vi tra loi dung
diem = diem + 5

# 3. Tru diem phat vi tra loi tre
diem = diem - 2

# 4. Xem ket qua cuoi cung
print(diem)
Thử đoán

Trước khi đọc tiếp: tự chạy bằng tay bốn dòng trên, dòng nào cũng tính lại từ giá trị ô diem NGAY LÚC ĐÓ. Dòng print(diem) in ra số nào?

Output thật:

13

Đối chiếu từng dòng (cột giữa là giá trị CŨ vế phải dùng để tính, cột cuối là giá trị MỚI vừa ghi vào ô):

Dòng vừa chạyVế phải tính bằng giá trị cũ nàoGiá trị mới ghi vào ô diem
diem = 10(chưa có giá trị cũ, đây là lần gán đầu tiên)10
diem = diem + 5diem cũ là 1010 + 5 = 1515
diem = diem - 2diem cũ là 1515 - 2 = 1313

Kết quả cuối cùng khớp output thật: diem = 13. Mỗi dòng chỉ nhìn thấy giá trị ô đang giữ NGAY LÚC dòng đó chạy.

5. Vì sao lập trình đọc dấu bằng khác toán học?

Toán họcLập trình
x = x + 1 vô nghiệm — không có x nào thoả mãnx = x + 1 hợp lệ — đây là một LỆNH, không phải một MỆNH ĐỀ cần giải
= khẳng định hai vế bằng nhau cùng một lúc, đúng cả hai chiều= là phép gán, chỉ chạy theo MỘT chiều: tính vế phải trước, chép sang vế trái sau
a = bb = a diễn đạt cùng một khẳng địnha = bb = a là hai lệnh khác nhau — chép giá trị theo hai chiều ngược nhau
Hiểu nhầm phổ biến

Khảo sát 97 giáo viên phổ thông (Chiodini và cộng sự, ICER 2025): bản thân giáo viên hiếm khi nhầm dấu bằng là phép so sánh (chỉ khoảng 3%), nhưng 30/97 người báo học sinh của mình mắc lỗi này thường xuyên. Phần này nghe "quá cơ bản" chính là cảm giác khiến người ta lướt qua rồi mắc đúng lỗi đó.

6. Pitfall tổng hợp

Nhầm 1 — sợ hoặc bỏ qua dòng x = x + 1 vì tưởng nó "sai về toán": người mới đọc code thường khựng lại ở đây, nghĩ tác giả viết nhầm.

✅ Đọc lại theo đúng thứ tự máy làm ở mục 2: lấy giá trị cũ, tính vế phải, ghi đè.

Nhầm 2 — tưởng ô nhớ giữ được cả giá trị cũ lẫn giá trị mới sau khi gán lại:

diem = 10
diem = diem + 5
# lam tuong diem "nho" ca 10 lan 15 - SAI

✅ Sau dòng thứ hai, ô diem chỉ còn giữ 15. Giá trị 10 mất hẳn. Nếu cần giữ lại giá trị cũ để dùng sau, phải chủ động chép nó sang một ô khác trước khi ghi đè:

# 1. Cat gia tri cu sang mot o rieng, TRUOC khi ghi de
diem_cu = diem

# 2. Bay gio moi cap nhat gia tri moi cho diem
diem = diem + 5

Giờ diem_cu giữ 10, còn diem giữ 15 — hai ô độc lập, không ô nào tự "nhớ" lịch sử của mình.

7. 📚 Đào sâu — vì sao phép gán khó hơn nó trông thế

📚 Đào sâu (tuỳ chọn)

Nghiên cứu về mô hình tư duy phép gán:

Ghi chú: cả hai đo trên người ĐÃ qua ít nhất một môn nhập môn — bài tưởng "quá cơ bản" này vẫn là nơi nhiều người kẹt lại.

8. Liên hệ các bài khác

9. Tóm tắt

  • Trace mọi dòng gán bằng đúng 3 bước ở mục 3, kể cả khi biến xuất hiện ở cả hai vế như trong x = x + 1; đừng cố giải nó như phương trình.
  • Debug một chuỗi gán dài bằng cách viết ra giá trị hiện tại của từng ô TRƯỚC khi đọc dòng kế tiếp — giá trị cũ có thể đã bị ghi đè từ dòng trước, trí nhớ dễ đánh lừa hơn bạn tưởng.
  • Trước khi ghi đè một ô, tự hỏi có cần giữ giá trị cũ không; nếu có, chép nó sang một ô khác trước khi gán — bản thân ô gốc không tự lưu lịch sử.

10. Tự kiểm tra

Tự kiểm tra
0/6 câu đã trả lời
  1. Q1
    Vì sao dòng lệnh x = x + 1 không hề mâu thuẫn với việc phương trình toán học cùng dạng vô nghiệm?
  2. Q2
    Đoạn sau ghi giá trị nào vào ô diem sau dòng cuối cùng?
    diem = 20
    diem = diem + 10
    diem = diem - 5
  3. Q3
    Sau khi chạy hai dòng y = 3 rồi y = y + y, ô y giữ giá trị bao nhiêu? Giải thích cơ chế, không chỉ nêu kết quả.
  4. Q4
    Bạn đọc đoạn code: a = 5 rồi b = a. Sau đó bạn đổi a = 9. Ô b có tự đổi theo thành 9 không? Vì sao?
  5. Q5
    Vì sao nói 'một ô chỉ giữ một giá trị tại một thời điểm' lại là lý do khiến bạn không thể hoán đổi giá trị của hai biến chỉ bằng hai dòng gán trực tiếp (a = b rồi b = a)?
  6. Q6
    Mô tả quy trình ba bước máy luôn thực hiện khi chạy MỘT dòng lệnh gán bất kỳ, không riêng gì x = x + 1.

Bài tiếp theo: Chuỗi và số

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Chuỗi và số — vì sao cộng hai kiểu lại nổ