例如:
void main(){
long a = 101010;
long *p = &a;
long b = p;
printf("%lld\n", *(long*)b);
}
它总是安全的吗?
评论:long b = p;
会产生一个警告:
Initialization makes integer from pointer without a cast
然而,该程序打印出
101010
。标准不能保证这样的类型转换总是有效的。
使用 intptr_t
(或它的无符号版本 uintptr_t
)将指针存储在整数类型中。它保证可以将 void *
指针转换为这些类型,并进行转换后得到相同的值。
请注意,这些类型是可选的。
ptrdiff_t
(带符号)和size_t
(无符号)相同,是吗? - technosaurussizeof(long)
通常由编译器定义(受底层硬件架构的影响)。
sizeof(long *)
取决于您平台上的虚拟内存地址空间大小。
例如,在64位操作系统和基于x64的处理器上使用Visual Studio编译器:
sizeof(long) == 4
sizeof(long*) == 8
因此:
long b = p
,只复制了p
的最低有效字节到b
*(long*)b
访问无效的内存地址是有可能的在这个例子中,如果p
的四个最高有效字节为0,则“不会造成伤害”。但由于不能保证这种情况,当sizeof(long)!=sizeof(long*)
时,此代码通常是不安全的。
此外,即使sizeof(long)==sizeof(long*)
,您仍应避免使用这种类型的转换(指针到整数),以便使代码可移植到其他平台。
更新
请注意,printf("%lld\n", *(long*)b)
也是不安全的。
您应该基本上使用"%ld"
来表示long
值,使用"%lld"
来表示long long
值。
如果sizeof(long) < sizeof(long long)
,则可能会导致运行���内存访问冲突。
为了保险起见,我们应该考虑它不安全。
在n位系统中指针的大小为n位。例如,在任何64位结构/编译器上,指针大小应为8字节。这是有保证的。
然而,数据类型没有这样的保证。它很大程度上取决于编译器。在你的情况下,两者恰好具有相同的大小,这就是为什么它能工作的原因。
编辑:
关于警告
Initialization makes integer from pointer without a cast
你的编译器在发出警告方面是非常正确的。请检查数据类型。一个 int *
变量不等于一个 int
变量。在你的情况下,它之所以能够工作,只是因为在你特定的情况/实现中长度相同。
即使p
值和b
值相同,只有p
可以访问a
,即可以指向a
,b
无法指向a
。因为p
被声明为指针,而b
被声明为变量。
您无法通过b
访问a
。那么您的问题如何适用?在b
中,a
的地址被视为仅是value
而不是address
,因为b
是变量而不是指针。
即使它打印101010
,这个程序也不是通用的。
type *var-name;
所有指针的实际数据类型,无论是整数、浮点数、字符或其他类型,都是相同的,即表示内存地址的长十六进制数。不同数据类型的指针之间唯一的区别在于指针所指向的变量或常量的数据类型。
指针通常具有固定的大小,例如在32位可执行文件上它们通常是32位。有一些例外情况,比如在旧的16位Windows系统中,你必须区分32位指针和16位指针... 在现代桌面操作系统中,通常可以安全地假设它们在给定的可执行文件中是统一的。
整数
,但它总是可以表示为一个整数
(它可能是一个int
或一个long
,甚至是一个30位的值,但你可以声明它是一个整数
)。 - Elliott Frisch