i've been thinking about null pointers
i’ve been thinking about null pointers
i’ve been thinking about null pointers 2026-07-15 我一直在思考空指针(null pointers) 2026-07-15
null is weird. you ever think about null pointers? because i’ve been thinking about null pointers. to be clear, when i say “null is weird”, i’m not talking about safety. the issue of null safety becomes almost non-existent if you use a competently designed programming language. Null 很奇怪。你思考过空指针吗?因为我一直在思考空指针。需要明确的是,当我提到“Null 很奇怪”时,我指的并不是安全性。如果你使用一种设计合理的编程语言,空指针安全问题几乎是不存在的。
no, i mean just, like, the entire concept of null is kinda weird. like, you have a pointer type. it points to something. it contains the address of the thing that it points to. but then there’s one (1) value which isn’t an address; it’s a method of communicating a piece of semantic information. 不,我的意思是,Null 这个概念本身就很奇怪。比如,你有一个指针类型,它指向某样东西,包含着所指向对象的地址。但偏偏有一个(1)值它不是地址;它是一种传达语义信息的方式。
and that information depends on the context: it’s often used in C to indicate that an error occurred, or in various languages you could use it to say that a thing that could exist doesn’t exist. and sometimes it communicates something else entirely, like, if you supply null to a function to tell it to allocate its own pointer rather than using a user-supplied one. 这种信息取决于上下文:在 C 语言中,它常被用来表示发生了错误;而在各种语言中,你可以用它来表示某个本该存在的东西实际上并不存在。有时它传达的信息完全不同,比如当你向函数传入 null,告诉它自行分配指针,而不是使用用户提供的指针时。
and this is all conveyed with just a single value. which leads to the other weird thing about null. the idea of pointer types in most languages is that a pointer could have any value, except for one, that being the null pointer. but anything else goes. 所有这些都仅通过一个值来传达。这就引出了 Null 的另一个奇怪之处。在大多数语言中,指针类型的概念是:指针可以取任何值,除了一个——即空指针。除此之外,其他值都可以。
in C in particular, the null pointer is pretty much specified as its own thing with completely different semantics than any other pointer. the thing is, in userspace, not every pointer value is a possible address. at least on linux, 64-bit pointers only use 48 bits, so everything above 1 << 47 is invalid. 特别是在 C 语言中,空指针被明确为一种特殊存在,其语义与任何其他指针完全不同。问题在于,在用户空间中,并非每个指针值都是合法的地址。至少在 Linux 上,64 位指针只使用了 48 位,因此所有大于 1 << 47 的值都是无效的。
i also assume that there’s plenty of low values that could never be valid. so like, let’s assume that a userspace pointer will never have an address lower than 0x1000. that’s a lot of different non-address values that a pointer could store! if we’re already storing semantic information with null, why stop there? 我还假设有很多较小的值永远不可能成为有效地址。所以,假设用户空间指针的地址永远不会低于 0x1000。那么指针可以存储大量非地址的值!既然我们已经用 Null 来存储语义信息了,为什么只停留在这一步呢?
i’ll use a function from Hare’s standard library as an example because Hare is a language i’m familiar with and it demonstrates the point i’m trying to make, but to be clear this isn’t intended as a language proposal for Hare; it’s just a thought experiment and maybe an idea for some other more experimental programming language: 我将以 Hare 标准库中的一个函数为例,因为 Hare 是我熟悉的语言,它能很好地说明我的观点。但需要明确的是,这并非针对 Hare 的语言提案;这只是一个思想实验,或许可以为其他更具实验性的编程语言提供思路:
bufio::scan_line has the return type (str | io::EOF | io::error | utf8::invalid), which is a tagged union. so the return type contains both a tag and a pointer (contained within str). but the pointer is only valid when the tag has a specific value. and all other types that could be returned are aliases of void (with the exception of io::error, which is a tagged union of void aliases, but that doesn’t really change things here).
bufio::scan_line 的返回类型是 (str | io::EOF | io::error | utf8::invalid),这是一个带标签的联合体(tagged union)。因此,返回类型既包含标签也包含指针(包含在 str 中)。但只有当标签具有特定值时,指针才有效。而其他所有可能返回的类型都是 void 的别名(除了 io::error,它是 void 别名的联合体,但这在这里并不影响结论)。
so the null pointer is used to represent an empty string, which this function could return. but we could also represent io::EOF as a “pointer” whose value is 0x1. and we could represent all the various io::error types as other pointer values that won’t ever point to real things. so the tag becomes completely unnecessary, and the return type can now fit into two 64-bit registers (pointer + length).
所以,空指针被用来表示该函数可能返回的空字符串。但我们也可以将 io::EOF 表示为一个值为 0x1 的“指针”。我们还可以将各种 io::error 类型表示为其他永远不会指向真实对象的指针值。这样一来,标签就完全没必要了,返回类型现在可以放入两个 64 位寄存器中(指针 + 长度)。
and that’s just one example. the idea is kinda like NaN boxing, except with pointers instead of floating point numbers, and also a bit more limited. the idea is basically that null already exists as a way to store some other value within a pointer type, and there’s no reason null has to be more special than any of the other impossible addresses, so why not just, add more values? idk, this is all just a thought experiment and it might be a terrible idea, null is weird. 这只是一个例子。这个想法有点像 NaN boxing,只不过是用指针代替了浮点数,而且限制更多一些。其核心思想是:Null 本身就已经作为一种在指针类型中存储其他值的方式存在了,没有理由让 Null 比其他任何“不可能的地址”更特殊,所以为什么不直接增加更多的值呢?不知道,这只是一个思想实验,也许是个糟糕的主意,Null 真奇怪。