Con trỏ rỗng

Trong ngành điện toán, khái niệm **con trỏ rỗng** chỉ một biến con trỏ có một giá trị định sẵn khiến cho nó không trỏ được tới bất kỳ một đối tượng chính tắc nào. Các ngôn ngữ lập trình thường sử dụng con trỏ rỗng trong trường hợp cuối một list không rõ độ dài hoặc một tác vụ nào đó thất bại; cách sử dụng con trỏ rỗng tương đương với các kiểu rỗng và ứng với giá trị *Nothing* trong kiểu lựa chọn.

Không nên nhầm lẫn con trỏ rỗng với một biến con trỏ chưa được khởi tạo giá trị cho nó: mục đích của việc sử dụng con trỏ rỗng là để để bảo đảm phép so sánh không bằng với bất kỳ con trỏ nào trỏ tới một đối tượng chính tắc. Tuy nhiên, tùy vào từng ngôn ngữ và phương pháp lập trình của nó, một biến con trỏ chưa được khởi tạo không chắc chắn sẽ thực hiện được yêu cầu trên. Khi thực hiện phép so sánh, nó có thể cho ra kết quả bằng với một giá trị nào đó, là một con trỏ chính tắc; hoặc có thể bằng với một con trỏ rỗng khác. Và như thế, biến con trỏ này có thể đóng cả hai vai trò ở những thời điểm khác nhau.

Về mặt ngữ nghĩa, con trỏ rỗng (null pointer) khác với giá trị rỗng (null value). Con trỏ rỗng trong hầu hết ngôn ngữ lập trình có nghĩa là "không có giá trị", trong khi giá trị rỗng trong một cơ sở dữ liệu quan hệ có nghĩa là "có giá trị không rõ". Điều này tạo ra một khác biệt trong thực tế: hầu hết ngôn ngữ lập trình xem hai con trỏ rỗng bằng nhau, nhưng các hệ cơ sở dữ liệu quan hệ lại không coi hai giá trị rỗng là bằng nhau(vì chúng biểu diễn các giá trị không rõ, và như thế, không thể khẳng định chúng có bằng nhau không).

## C

Trong C, hai con trỏ rỗng của bất kỳ kiểu nào cũng được đảm bảo là bằng nhau. Macro `NULL` được dùng để định nghĩa một hằng số con trỏ rỗng, mà trong tiêu chuẩn C99 có thể linh hoạt xem như một giá trị kiểu nguyên `0` được chuyển đổi tường minh hoặc bất tường minh qua kiểu `void*`.

Tham chiếu ngược một con trỏ rỗng có thể dẫn đến kết quả là hệ thống sẽ cố gắng đọc hoặc ghi in từ một vùng nhớ không được trỏ tới - gây ra lỗi phân đoạn hoặc xâm phạm truy cập (access violation). Khi đó, chương trình sẽ gặp lỗi hỏng hóc hoặc một ngoại lệ (exception) sẽ được "ném" ra. Tuy vậy, trong một số trường hợp, đây không phải là vấn đề nặng nề. Thí dụ, với các máy x86, địa chỉ 0000:0000 có thể được đọc và thường là có thể ghi được, cho nên, phép tham chiếu ngược biến con trỏ rỗng là hoàn toàn hợp lệ, một số hệ quả không mong muốn tuy không dẫn đến sập hệ thống nhưng cũng gây ra lỗi thiếu định nghĩa (undefined). Nên chú ý là cũng có những trường hợp khiến cho tham chiếu ngược con trỏ rỗng là có chủ đích và được định nghĩa tốt; ví dụ như mã BIOS viết bằng ngôn ngữ C cho các thiết bị x86 16-bit có thể viết IDT ở địa chỉ vật lý 0 của máy bằng cách sử dụng phép toán tham chiếu ngược một con trỏ rỗng. Trình biên dịch có thể tối ưu hóa bằng cách bằng cách không biên dịch phép tham chiếu ngược đối với con trỏ rỗng, nhằm ngặn chặn lỗi phân đoạn tuy nhiên vẫn có thể gây ra những hành vi nguy cơ khác

## C++

Trong ngôn ngữ lập trình C++, macro `NULL` được kế thừa từ C, giá trị kiểu số nguyên zero được ưu tiên dùng để thể hiện một hằng số con trỏ. Tuy vậy, C++11 giới thiệu một giá trị hằng số con trỏ <code>nullptr</code> để sử dụng thay thế.

## Ngôn ngữ khác

Trong một số môi trường lập trình khác (at least one proprietary Lisp implementation, for example), giá trị được dùng làm con trỏ rỗng (được gọi là `nil` trong Lisp) có thể thực sự là một con trỏ trỏ đến một khối dữ liệu nội hàm được sử dụng trong một hiện thực hóa nào đấy (nhưng không dễ dàng mà một chương trình người dùng có thể dễ dàng truy cập), thế là, bằng một register, người ta có thể sử dụng nó làm một hằng số hữu ích và lấy nó làm cách truy cập nội hàm hiện thực hóa. Ta gọi đây là *vector <code>nil</code>*.

Với các ngôn ngữ sử dụng kiến trúc thẻ, một tagged union mà việc sử dụng nó bắt buộc phải quy định tường minh cách thức xử lý các trường hợp ngoại lệ có thể dùng để thay thế con trỏ rỗng; trong thực tế, con trỏ rỗng có thể được xem là một con trỏ được gắn thẻ và thẻ này có thể tính toán được.

## Tham chiếu ngược

Vì con trỏ rỗng không tham chiếu tới bất kỳ một đối tượng có nghĩa nào, cố gắng thực hiện tham chiếu ngược (truy cập tới dữ liệu được lưu ở vùng nhớ đó) con trỏ rỗng thường (nhưng không phải luôn luôn) gây ra lỗi thời gian thực thi hoặc có thể khiến cho chương trình bị hư hại.
* Trong ngôn ngữ lập trình C, hành vi tham chiếu ngược cho biến con trỏ rỗng chưa được định nghĩa. Thực hiện phép toán này sẽ khiến cho chương trình bị dừng lại và gây nên lỗi phân đoạn, bởi vì việc sử dụng con trỏ rỗng để chỉ ra một địa chỉ không bao giờ được hệ thống cấp phát khu nhớ để lưu bất kỳ đối tượng nào. Tuy vậy, hành vi này không mang tính toàn cục.
* Đối với ngôn ngữ lập trình Java, tham chiếu đến một đối tượng rỗng sẽ sinh ra ngoại lệ  (NPE), ngoại lệ này có thể được bắt giữ xử lý qua mã, nhưng trong thực tiễn, phương pháp được ưu tiên là người lập trình cần phải tính toán cẩn thận để kiểu ngoại lệ này không bao giờ xảy ra.
* Đối với môi trường.NET, tham chiếu đến một đối tượng rỗng sẽ phát sinh ngoại lệ NullReferenceException. Mặc dù bắt giữ ngoại lệ dạng này không được xem là cách thức lập trình tốt, chương trình vẫn có thể bắt và xử lý ngoại lệ này.
* Trong ngôn ngữ Objective-C, thông tin có thể gởi tới một đối tượng `nil` (được hiểu là một con trỏ rỗng) mà không khiến cho chương trình phải dừng đột ngột; thông tin này đơn giản là được bỏ qua, giá trị trả về (nếu có) là `nil` hoặc `0`, tùy vào kiểu dữ liệu.

## Lược sử

Năm 2009 C.A.R. Hoare cho biết
chính ông là người sáng tạo ra tham chiếu rỗng vào năm 1965 khi tạo ra ngôn ngữ lập trình Algol W, mặc dù NIL đã tồn tại trong Lisp kể từ năm 1959. Trong lần đề cập năm 2009 đó, Hoare nói rằng phát minh của ông là một "sai lầm tỉ đô":

## Tham khảo

## Liên kết ngoài
*.