Nội dung bài viết
Đăng ký học lập trình C++
Tại STDIO bạn được dạy nền tảng lập trình tốt nhất.
Đăng ký học
So sánh null, NULL và rỗng ("_") - null là không có gì, rỗng là tồn tại cái không có, một gút mắt mà ta phải lựa chọn trong khi thiết kế. Bài viết nhằm mục đích phân biệt rõ ràng các khái niệm trên để chương trình an toàn hơn.

Giới thiệu

KHÔNG TỒN TẠITỒN TẠI CÁI KHÔNG là 2 khái niệm khác nhau nhưng dễ gây nhầm lẫn. Trong nhiều ngôn ngữ lập trình hay các công việc chung hơn cũng tồn tại 2 "tư duy" này. Với lý thuyết của khoa học tự nhiên và giới hạn hơn trong lĩnh vực lập trình này, tôi chỉ mang 1 khía cạnh nhỏ trong lý thuyết KHÔNG TỒN TẠITỒN TẠI CÁI KHÔNG CÓ GÌ.

Tôi dùng chủ yếu kiểu dữ liệu string để giải thích cho 2 khái niệm này và không giải thích khái niệm thứ 3: KHÔNG TỒN TẠITỒN TẠI CÁI KHÔNG CÓ GÌTỒN TẠI CÁI KHÔNG.

Tiền đề bài viết

Trong cuộc sống và mọi lĩnh vực không chỉ lập trình, 2 khái niệm KHÔNG TỒN TẠITỒN TẠI CÁI KHÔNG CÓ GÌ gây không ít nhầm lẫn và rắc rối, mặc dù nhận thấy nó từ lâu, hôm nay tôi mới mang nó ra để phân tích với mong muốn các bạn sẽ tìm được giải pháp tốt khi thiết kế vì thông qua training, tôi vẫn thấy các trainee của mình lầm lẫn giữa 2 khái niệm này.

Đối tượng hướng đến

Đây là kinh nghiệm cá nhân, nên sẽ có thể có nhiều quan điểm khác nhau, mong nhận được nhiều quan điểm của nhiều người để giúp cho các thế hệ kế tiếp được hưởng thành quả đầy đủ hơn từ các thế hệ đi trước.

null và rỗng?

Xét ví dụ một tham chiếu kiểu string trong ngôn ngữ C#, ta có 2 lựa chọn như sau

string str_a = null; // null
string str_b = ""; // rỗng

Dòng code đầu tiên biểu thị của việc không có đối tượng kiểu string tồn tại trong bộ nhớ - KHÔNG TỒN TẠI. Dòng code thứ hai biểu thị có tồn tại đối tượng string trong bộ nhớ và dữ liệu của đối tượng này đang có là một chuỗi không chứa ký tự nào cả - TỒN TẠI CÁI KHÔNG CÓ GÌ.

Điều khác biệt thứ 2 của 2 dòng codes trên là str_a chứa một thông tin để ta có thể kiểm tra được là đối tượng str_a chưa tồn tại, còn str_b thì đã tồn tại rồi. Nếu ta xem các dòng code trên là SỰ KHỞI ĐẦU của một biến thì dường như str_b là một sự thiên vị khi ta hy sinh 1 giá trị (rỗng) để đánh dấu cho sự khởi đầu của str_b, trong khi đó chính ngôn ngữ lập trình đã thiết kế sẵn cho ta null để đánh dấu như một trường hợp đặc biệt.

null hay rỗng khi thực thi thất bại?

Đặt trường hợp ta có một hàm thực thi một nhiệm vụ và sẽ trả về một đối tượng. Vậy đối tượng trả về sẽ là KHÔNG CÓ ĐỐI TƯỢNG NÀO hay là có một đối tượng với tất cả thuộc tính mặc định (0, -1 hoặc "") do ta tự quy ước?

Có một quan điểm cho rằng, nếu hàm trả về null thì xác suất gây ra lỗi thực thi cao hơn trả về một giá trị mặc định, thường là rỗng. Xem xét codes bên dưới mà quan điểm này đề cập

StdioDevice3D device = CreateGraphicDevice();

device.PerformDraw();

Nếu CreateGraphicDevice() khởi tạo thất bại sẽ trả về null, nếu devicenull thì không có đối tượng tồn tại và gây ra lỗi (crash) khi gọi PerformDraw(). Với cá nhân tôi thì quan điểm này không thuyết phục vì nếu là người có kinh nghiệm, ta sẽ kiểm tra rất chặt chẽ ứng dụng

StdioDevice3D device = CreateGraphicDevice();

if (device != null)
{
	device.performDraw();
}

NULL và nullptr trong C++11

Trong C++03 trở về trước, giá trị 0 (NULL) được "hy sinh" để đánh dấu cho việc 1 pointer chưa trỏ tới đâu. Bản thân hệ thống cũng được "dạy" rằng không được cấp phát tại vùng nhớ có địa chỉ 0. Chúng ta không xét ở khía cạnh của mã máy, chỉ tập trung xét ở khía cạnh của của ngôn ngữ C++ thì đến phiên bản C++11 mới cho ra đời khái niệm nullptr chuyên đánh dấu cho khái niệm chưa tồn tại của vùng nhớ.

Để hiểu rõ hơn về nullptr các bạn có thể đọc thêm bài C++11 - nullptr.

Theo cá nhân, tôi thiên về hướng sử dụng null để đánh dấu cho việc không tồn tại hơn là rỗng hay mặc định vì nó được sinh ra để làm việc đó. Tuy nhiên, trong một số dự án cụ thể, có thể chúng ta mới biết cụ thể chọn phương án nào để mục tiêu cuối cùng cũng là sản phẩm ít rủi ro nhất.

THẢO LUẬN