C语言中不同类型的两个独立变量的地址是否可能相等?
首先直接给你结论:对于代码里定义的两个独立变量x和y,你写的三个断言全部都会永远成立,C标准完全保证这一点。下面逐一解释原因,以及你关心的那些假设场景为什么不可能出现。
核心问题的解答
1. void*和char*的指针比较
C标准的对象模型规定:每个独立的对象(变量属于对象的一种)都拥有唯一的存储位置。当你把指向不同对象的指针转换为void*后进行比较,结果一定是不相等的——因为void*是通用指针类型,它的比较语义就是直接对比对象的存储位置唯一性。
同样,char*的比较和void*语义完全一致:char*允许用来访问任何对象的原始字节,指向不同对象的char*指针,必然指向不同的存储位置,所以比较结果一定不等。
所以这两个断言:
assert((void*)(&x) != (void*)(&y)); // 必然通过 assert((char*)(&x) != (char*)(&y)); // 必然通过
是绝对安全的,不会有任何符合C标准的编译器让它们失败。
2. 指针转换为整数类型后的比较
对于(uint64_t)(&x) != (uint64_t)(&y)这个断言,只要你的系统中uint64_t是适合存储指针的类型(比如系统的uintptr_t就是uint64_t,或者sizeof(uint64_t) >= sizeof(void*)、指针可以无损转换为该整数),那么这个断言也一定会成立。
C标准规定:如果一个指针转换为整数类型,再转换回原指针类型,必须能精确指向原来的对象。如果两个不同对象的指针转成整数后相等,那么把这个整数转回去就会指向同一个对象,这就违反了“不同对象有唯一存储位置”的规则。因此,符合标准的编译器必须保证,不同对象的指针转成合适的整数类型后,值一定不同。
关于你假设的特殊硬件场景
你担心的“不同类型变量存在不同内存区域(比如GPU内存和CPU内存)但地址数值相同”的情况,C标准明确禁止这种情况出现在C语言的抽象模型中。
不管底层硬件怎么实现,编译器必须从C语言的视角隐藏这些细节——也就是说,在C代码的逻辑里,每个对象的地址是唯一的标识,和它的类型无关。不存在“同一地址但类型不同就指向不同内存区域”的情况,因为这会彻底破坏C的对象模型和指针的语义。
举个例子:如果真的有这种硬件,编译器必须为不同类型的变量分配不同的、可以区分的地址(比如给GPU内存的变量地址加上一个偏移量),保证在C代码里,不同对象的地址比较结果是正确的。
结合你找到的标准条款和资料
你提到的C23标准6.5.9条款:“如果两个指向对象类型的指针都指向同一个对象,或者都指向同一个数组对象的最后一个元素之后的位置,则它们比较相等”。其实它的逆命题也是成立的——如果两个void*指针比较相等,那么它们一定指向同一个对象(或者同一数组的尾后位置)。所以反过来,指向不同对象的void*指针一定不相等。
至于你看到的C里的备注,C和C的对象模型有差异。C++允许一些更复杂的对象模型场景,但C语言的对象模型更简单直接,严格保证每个独立对象的存储位置唯一性,不存在“同地址不同对象”的合法情况。
总结
所有你列出的断言,在符合C标准的环境下都会通过。C标准从根本上保证了不同独立对象的地址(无论是作为通用指针、字符指针还是合适的整数类型)都是唯一且可区分的。
备注:内容来源于stack exchange,提问作者Adam Sho

