我应该使用哪种格式说明符来打印变量的地址?我分不清下面这批货。
%u -无符号整数 %x -十六进制值 %p -空指针
%u -无符号整数
%x -十六进制值
%p -空指针
打印地址的最佳格式是什么?
p是打印指针的转换说明符。用这个。
p
int a = 42; printf("%p\n", (void *) &a);
请记住,省略强制转换是未定义的行为,并且使用p转换说明符的打印是以实现定义的方式完成的。
使用%p作为“指针”,不要使用其他任何东西*。标准不能保证你可以像对待任何特定类型的整数一样对待指针,所以你实际上会得到积分格式的未定义行为。(例如,%u期望unsigned int,但如果void*的大小或对齐要求与unsigned int不同怎么办?)
%p
%u
unsigned int
void*
参见乔纳森的精彩回答!作为%p的选择,你可以使用C99中添加的来自<inttypes.h>的指针特定宏。
<inttypes.h>
在C语言中,所有对象指针都可以隐式转换为void*,但为了将指针作为可变参数传递,必须显式强制转换它(因为任意对象指针只能是可转换,而不是相同的):
printf("x lives at %p.\n", (void*)&x);
假设你不介意不同平台之间格式的变化,最简单的答案是标准的%p符号。
C99标准(ISO/IEC 9899:1999)在§7.19.6.1中说¶8:
p实参应该是指向void的指针。指针的值为 在实现定义中转换为打印字符序列 方式。< / p >
void
(在C11 - ISO/IEC 9899:2011 -信息在§7.21.6.1¶8。)
在某些平台上,这将包括前导0x,而在其他平台上则不会,字母可以是小写或大写,C标准甚至没有定义它应该是十六进制输出,尽管我知道没有任何实现不是这样的。
0x
是否应该显式地使用(void *)类型转换指针还有待讨论。它是显式的,这通常是好的(所以这是我所做的),标准说“参数应该是一个指向void的指针”。在大多数机器上,可以省略显式类型转换。然而,如果给定内存位置的char *地址的位表示形式与相同内存位置的'还有其他指针吗'地址不同,那么这就很重要了。这将是一个字寻址,而不是字节寻址的机器。这样的机器现在并不常见(可能没有),但我大学毕业后使用的第一台机器就是这样的(ICL Perq)。
(void *)
char *
如果你对%p的实现定义的行为不满意,那么使用C99 <inttypes.h>和uintptr_t:
uintptr_t
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
这允许您微调表示以适合自己。我选择用大写的十六进制数字,以便数字具有统一的高度,并且在0xA1B2CDEF的开头出现特征下降,而不像0xa1b2cdef那样沿着数字上下下降。你的选择范围很广。当GCC可以在编译时读取格式字符串时,它明确建议使用(uintptr_t)强制转换。我认为要求打石膏是正确的,尽管我相信有一些人会无视警告,大多数时候都能逃脱。
0xA1B2CDEF
0xa1b2cdef
(uintptr_t)
Kerrek在评论中问道:
我对标准推广和可变参数有点困惑。是否所有指针都被标准提升为void*?否则,如果int*是两个字节,而void*是四个字节,那么从参数中读取四个字节显然是一个错误,不是吗?
int*
我误以为C标准规定所有对象指针的大小必须相同,所以void *和int *的大小不能不同。然而,我认为C99标准的相关部分并不是那么强调(尽管我不知道有哪个实现是我建议的是真的,但实际上是假的):
void *
int *
§6.2.5类型 指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求。类似地,指向兼容类型的合格或不合格版本的指针应具有相同的表示和对齐要求。所有指向结构类型的指针都应该具有相同的表示方式和对齐要求。所有指向联合类型的指针都应该具有相同的表示方式和对齐要求。指向其他类型的指针不需要具有相同的表示或对齐要求。 39)相同的表示和对齐要求意味着函数参数、函数返回值和联合成员的互换性。
§6.2.5类型
指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求。类似地,指向兼容类型的合格或不合格版本的指针应具有相同的表示和对齐要求。所有指向结构类型的指针都应该具有相同的表示方式和对齐要求。所有指向联合类型的指针都应该具有相同的表示方式和对齐要求。指向其他类型的指针不需要具有相同的表示或对齐要求。
39)相同的表示和对齐要求意味着函数参数、函数返回值和联合成员的互换性。
(C11在§6.2.5节、¶28和脚注48中说的完全相同。)
因此,所有指向结构的指针必须彼此大小相同,并且必须共享相同的对齐要求,即使指针所指向的结构可能具有不同的对齐要求。工会也是如此。字符指针和空指针必须具有相同的大小和对齐要求。指向int变体(即unsigned int和signed int)的指针必须彼此具有相同的大小和对齐要求;其他类型的情况也类似。但是C标准并没有正式地说sizeof(int *) == sizeof(void *). C。哦,SO有助于你检查你的假设。
int
signed int
sizeof(int *) == sizeof(void *)
C标准明确不要求函数指针与对象指针大小相同。为了不破坏类dos系统上的不同内存模型,这样做是必要的。你可以有16位数据指针,但32位函数指针,反之亦然。这就是为什么C标准没有规定函数指针可以转换为对象指针,反之亦然。
幸运的是(对于以POSIX为目标的程序员),POSIX打破了这一规则,要求函数指针和数据指针的大小相同:
§2.12.3 指针类型 所有函数指针类型都应与指向void的类型指针具有相同的表示形式。将函数指针转换为void *不会改变其表示形式。由这种转换产生的void *值可以使用显式强制转换转换回原始的函数指针类型,而不会丢失信息。 < p >注意: ISO C标准不要求这一点,但POSIX一致性要求这一点
§2.12.3 指针类型
所有函数指针类型都应与指向void的类型指针具有相同的表示形式。将函数指针转换为void *不会改变其表示形式。由这种转换产生的void *值可以使用显式强制转换转换回原始的函数指针类型,而不会丢失信息。
因此,在将指针传递给诸如printf()这样的可变函数时,显式强制转换为void *似乎是非常可取的,以便在代码中获得最大的可靠性。在POSIX系统上,将函数指针强制转换为空指针进行打印是安全的。在其他系统上,这样做不一定安全,传递void *以外的指针而不进行强制转换也不一定安全。
printf()
作为其他(非常好的)答案的替代,你可以转换为uintptr_t或intptr_t(从stdint.h/inttypes.h),并使用相应的整数转换说明符。这将使指针的格式更加灵活,但严格来说,实现并不需要提供这些typedef。
intptr_t
stdint.h
inttypes.h
你可以使用%x或%X或%p;他们都是正确的。
%x
%X
a3bfbc4
A3BFBC4
这两个都是正确的。
如果你使用%x或%X,它会考虑地址的六个位置,如果你使用%p,它会考虑地址的八个位置。例如:
.