Thứ Ba, 31 tháng 3, 2026

Con trỏ NULL trong C – kẻ giết người thầm lặng mà ai cũng từng dính

Nếu bạn đã từng code C một thời gian (hoặc mới hôm qua), có thể bạn đã gặp một tình huống… "tình là tình nhiều khi không mà có, tình là tình nhiều lúc có như không":

Bạn hoàn tất một "công trình thế kỷ". Code nhìn hoàn toàn rõ ràng, rất hợp lý, rất ổn!
Nếu chỉ chạy Compile sẽ thấy không lỗi. Rõ ràng là không có vấn đề gì!

Nhưng nếu bấm Run…

Và… đùng một phát! Bạn gặp lỗi segmentation fault

Bạn thẫn thờ ngồi nhìn màn hình, tự hỏi:
"Ủa mình sai ở đâu vậy trời???"...Bấm Run lại...Vũ Như Cẩn, vẫn gặp lỗi!

Có thể khả năng cao: bạn vừa hội ngộ với một "em đẹp" tên là "con trỏ NULL".


Con trỏ NULL là gì?

Nói đơn giản, dễ hiểu, không màu mè:
Con trỏ NULL là con trỏ không trỏ đến đâu cả. Quá xá là đơn giản phải không?!


Trong C:
int *p = NULL;

Nghĩa là:
p tồn tại
Nhưng nó không trỏ tới bất kỳ vùng nhớ hợp lệ nào



Ví dụ thực tế 

Bạn tưởng tượng:
Con trỏ = địa chỉ nhà
NULL = "không có địa chỉ"

Bạn đang cầm một tờ giấy ghi:
"Nhà của tôi ở… (để trống)"

Và bạn vẫn cố...đấm ăn xôi:
  • gửi thư
  • giao hàng
  • đến thăm

Kết quả: ...chẳng có kết quả nào cả! Hư không...!



Lỗi kinh điển 


int *p = NULL;
*p = 10; // crash


Bạn đang ghi dữ liệu vào… "hư không"

Hệ điều hành nhìn thấy và nói:
"Không được phép!"
Và crash ngay lập tức


Vì sao con trỏ NULL nguy hiểm?

Vì nó trông có vẻ hợp lệ, và nó gật gù cho qua nếu bạn dịch code.
Code compile OK
Không warning
Nhìn bằng mắt thường cũng thấy "có vẻ đúng"

Nhưng runtime thì… "tai biến"


5 tình huống phổ biến gây ra NULL pointer

1. Quên cấp phát bộ nhớ
int *p;
*p = 5; // crash


p chưa trỏ tới đâu cả

2. malloc thất bại (ít gặp nhưng rất nguy hiểm)
int *p = malloc(sizeof(int));
if (p == NULL) {
    // hết bộ nhớ
}


Nếu bạn không check --> rất dễ crash

3. Hàm trả về NULL nhưng không kiểm tra
int *getData() {
    return NULL;
}
int *p = getData();
*p = 10; // crash


Tin tưởng mù quáng là một cái giá đắt 

4. Sau khi free mà quên set NULL
int *p = malloc(sizeof(int));
free(p);
*p = 10; // crash


Đây gọi là "dangling pointer" (không hẳn NULL nhưng nguy hiểm tương tự)

Best practice:
free(p);
p = NULL;


5. Truyền pointer sai giữa các hàm
Đây là kiểu lỗi "cao cấp hơn một chút" nhưng rất hay gặp khi code lớn



Cách tránh NULL pointer 

1. Luôn kiểm tra trước khi dùng
if (p != NULL) {
    *p = 10;
}


2. Sau khi free --> set NULL
free(p);
p = NULL;


Tránh "xài lại đồ cũ"

3. Khi dùng malloc --> luôn check
int *p = malloc(sizeof(int));
if (p == NULL) {
    printf("Hết bộ nhớ rồi!\n");
}


Để hiểu rõ hơn, bạn nên thử chạy code và "mục sở thị"
Bạn copy đoạn này vào: Online C Compiler

#include <stdio.h>
int main() {
    int *p = NULL;
    printf("Trước khi crash\n");
    *p = 10; // crash
    printf("Sau khi crash\n");
    return 0;
}



Kết quả:
Chỉ in ra đoạn Text: "Trước khi crash"
Và tiếp theo là Program finished with exit code 139

Điều đó có nghĩa là đoạn code "*p = 10; " đã "có vấn đề"

Sửa lại đoạn code như sau:
#include <stdio.h>
int main() {
    int *p = NULL;
    
    printf("Trước khi crash\n");
    printf("p = %p\n", p);
    *p = 10; // crash
    printf("Sau khi crash\n");
    
    return 0;
}


Nếu thấy:
p = (nil)
Bingo — Bạn đã tìn ra lỗi NULL pointer


Một sự thật biết rồi, khổ lắm, nói mãi

Rất nhiều bug khó chịu trong C đến từ:
  • pointer
  • NULL
  • memory
Nếu bạn thấy rối:
  • Bạn không cô đơn 



Tổng kết

Con trỏ NULL không phải là "lỗi khó"
Nó chỉ là một cái bẫy rất dễ dính

Chỉ cần nhớ:
NULL = không trỏ đi đâu
Dùng NULL = crash
Luôn kiểm tra trước khi dùng


FAQ nhanh

NULL pointer có luôn gây segmentation fault không?
Gần như luôn luôn nếu bạn dereference (*p)

Làm sao biết pointer có NULL không?
In ra hoặc check if (p == NULL)

Có nên tránh dùng pointer không?
Không. Nhưng phải dùng cẩn thận