有几种方法可以回答这个问题。
我们说指针值是内存位置的地址。但是不同的计算机对内存使用了不同的寻址方案。 C 是一种高级语言,可在多种计算机上移植。 C 不要求特定的内存架构。就 C 编程语言而言,内存地址实际上可以是“第四大道 123 号”之类的东西,很难想象在整数和这样的地址之间来回转换。
现在,对于您可能使用的任何机器,内存实际上是以一种相当简单且不足为奇的方式线性寻址的。如果你的程序有 1000 字节的可用内存,那么这些字节的地址范围可能从 0 到 999。所以如果你说
char *cp = (char *)10;
您只是设置了一个指向位于地址 10 的字节(或者,即程序地址空间中的第 11 个字节)的指针。
现在,在 C 语言中,指针不仅仅是内存中某个位置的原始地址。在 C 中,还声明了一个指针来指定它指向的数据类型。所以如果我们说
int *ip = (int *)10;
我们正在设置一个指向位于地址 10 的 int 值数据的指针。它与 cp 指向的内存中的点相同,但由于它是一个 int 指针,它将访问一个 int 值的字节,没有一个字节像cp 那样。如果我们在一台旧的 16 位机器上,并且 int 是两个字节,我们可以认为 ip 指向我们地址空间中的第五个 int。
C 中的强制转换实际上可以做两件事:(1) 转换一个值(“更改位”),或 (2) 更改一个值的解释。如果我们说float f = (float)3;,我们将在 3 的整数表示和 3 的浮点表示之间进行转换,这可能是完全不同的。如果我们朝另一个方向前进,比如int i = (int)3.14;,我们也会丢弃小数部分,所以会有更多的转换。但是如果我们说int *ip = (int *)10;,我们实际上并没有对值 10 做任何事情,我们只是将它重新解释为一个指针。如果我们说char *cp = (char *)ip,我们又没有改变任何东西,我们只是重新解释了一种不同的指针。
不过,我赶紧补充一下,我在这里所说的关于指针转换的所有内容都是 (a) 非常低级且依赖于机器的,并且 (b) 不是普通 C 程序员应该具备的那种东西在普通的编程任务中考虑,以及 (c) C 语言不保证。
特别是,即使在为具有传统线性寻址内存模型的计算机编程时,您的程序也可能无法访问地址 10,因此这些指针(cp 和 ip)可能是非常没用,如果您尝试使用它们可能会产生异常。 (另外,当我们有一个像 ip 这样指向超过 1 个字节的指针时,就会出现它指向哪些字节的问题。如果 ip 是 10,它可能指向 16 位上的字节 10 和 11 ,字节寻址的机器,但是这两个字节中哪个是int的低位,哪个是高位?这取决于它是“big endian”还是“little endian”机器。)
然后我们来到空指针。当您使用常量“0”作为指针值时,情况会有所不同。如果你说
void *p = (void *)0;
严格来说,您并不是在说“使p 指向地址0”。相反,您是在说“使 p 成为空指针”。但事实证明这与强制转换无关,这是因为语言中的一种特殊情况:在指针上下文中,常量 0 表示 空指针常量。
空指针是一个特殊的指针值,它被定义为不指向任何地方。它可能在内部表示为指向地址 0 的指针,也可能以其他方式表示。 (如果它实际上表示为指向地址 0 的指针,您的编译器会小心安排在地址 0 处永远不会有任何实际数据,因此即使指针指向地址 0,指针“不指向任何地方”仍然是正确的. 这有点令人困惑,对此感到抱歉。)
虽然指向像10 这样的原始地址的指针是低级的、危险的并且依赖于机器,但空指针是定义明确的并且非常好。例如,当您调用malloc 并且它无法为您提供所需的内存时,它会返回一个空指针来告诉您。当您测试malloc 的返回值以查看它是成功还是失败时,您只需检查它是否给了您一个空指针,并且没有任何低级、不可移植或不鼓励这样做。
有关这一切的更多信息,请参阅http://c-faq.com/null/index.html。