正确的格式说明符打印指针或地址?

我应该使用哪种格式说明符来打印变量的地址? 下面很多我很困惑。

%u – 无符号整数

%x – hex值

%p – 无效指针

哪个是打印地址的最佳格式?

最简单的答案,假设您不介意不同平台之间的变幻莫测和格式变化,是标准的%p表示法。

C99标准(ISO / IEC 9899:1999)在§7.19.6.1¶8中说:

p参数应该是指向void的指针。 指针的值以实现定义的方式转换为打印字符序列。

(在C11 – ISO / IEC 9899:2011中 – 信息在§7.21.6.1¶8中。)

在某些平台上,这将包括一个前导0x而在其他平台上它不会,并且字母可能是小写或大写,而C标准甚至没有定义它应该是hex输出,虽然我知道没有实施的地方。

您是否应该使用(void *)强制转换显式转换指针,这有点争论。 它是明确的,通常是好的(所以我这样做),标准说’论证应该是指向void的指针’。 在大多数机器上,你可以省略一个明确的演员表。 但是,它在一台机器上很重要,其中给定存储器位置的char *地址的位表示与同一存储器位置的“ 任何其他指针 ”地址不同。 这将是一个字寻址,而不是字节寻址的机器。 这些机器现在并不常见(可能没有),但我在大学gradle后使用的第一台机器就是其中之一(ICL Perq)。

如果您对%p的实现定义行为不满意,那么请使用C99 uintptr_t

 printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer); 

这允许您微调表示以适合自己。 我选择使用大写的hex数字,这样数字的高度一致,并且因此出现0xA1B2CDEF开头的特征下降,而不像0xa1b2cdef那样沿着数字向上和向下倾斜。 您的选择虽然在很宽的范围内。 GCC在编译时可以读取格式字符串时,明确推荐使用(uintptr_t) 。 我认为请求演员是正确的,但我确信有些人会忽略警告并且大部分时间都会逃脱警告。


Kerrek在评论中问道:

我对标准促销和可变参数有点困惑。 所有指针都被标准提升为void *吗? 否则,如果int*是两个字节,而void*是4个字节,那么从参数读取四个字节显然是错误的,非?

我认为C标准说所有对象指针都必须是相同的大小,所以void *int *不能是不同的大小。 但是,我认为C99标准的相关部分并不是那么强调(尽管我不知道我所建议的是真实的实现是错误的):

§6.2.5类型

¶26指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求。 39)类似地,指向兼容类型的合格或非限定版本的指针应具有相同的表示和对齐要求。 所有指向结构类型的指针都应具有相同的表示和对齐要求。 所有指向union类型的指针都应具有相同的表示和对齐要求。 指向其他类型的指针不需要具有相同的表示或对齐要求。

39)相同的表示和对齐要求意味着可互换性作为函数的参数,函数的返回值和联合的成员。

(C11在§6.2.5,¶28和脚注48节中完全相同)

因此,所有指向结构的指针必须具有相同的大小,并且必须共享相同的对齐要求,即使指针指向的结构可能具有不同的对齐要求。 工会也是如此。 字符指针和void指针必须具有相同的大小和对齐要求。 指向int上的变体的指针(表示unsigned intsigned int )必须具有相同的大小和对齐要求; 其他类型也是如此。 但是C标准并没有正式说明sizeof(int *) == sizeof(void *) 。 哦,这样做有助于你检查你的假设。

C标准最终不要求函数指针与对象指针的大小相同。 有必要不破坏DOS类系统上的不同内存模型。 在那里你可以有16位数据指针但是32位函数指针,反之亦然。 这就是C标准没有强制要求将函数指针转换为对象指针的原因,反之亦然。

幸运的是(对于针对POSIX的程序员),POSIX进入了违规行为并且确实要求函数指针和数据指针大小相同:

§2.12.3 指针类型

所有函数指针类型应与void指向的类型指针具有相同的表示forms。 将函数指针转换为void *不得改变表示。 这种转换产生的void *值可以使用显式转换转换回原始函数指针类型,而不会丢失信息。

注意:ISO C标准不要求这样,但它是POSIX一致性所必需的。

因此,当将指针传递给variadic函数(如printf()时,似乎强烈建议将显式转换为void *以便在代码中获得最大可靠性。 在POSIX系统上,将函数指针强制转换为void指针进行打印是安全的。 在其他系统上,这样做并不一定安全,在没有强制转换的情况下传递除void *之外的指针也不一定安全。

p是打印指针的转换说明符。 用这个。

 int a = 42; printf("%p\n", (void *) &a); 

请记住,省略强制转换是未定义的行为,而使用p转换说明符的打印是以实现定义的方式完成的。

使用%p表示“指针”,不要使用任何其他*。 标准不保证您可以像任何特定类型的整数那样处理指针,因此您实际上会使用整数格式获得未定义的行为。 (例如, %u期望一个unsigned int ,但是如果void*具有与unsigned int不同的大小或对齐要求,那该怎么办?)

*)[请参阅Jonathan的正确答案!]除了%p ,您还可以使用C99中添加的特定于指针的宏。

所有对象指针都可以在C中隐式转换为void* ,但是为了将指针作为可变参数传递,您必须显式地转换它(因为任意对象指针只能转换 ,但 void指针不同):

 printf("x lives at %p.\n", (void*)&x); 

作为其他(非常好)答案的替代方法,您可以uintptr_tuintptr_tintptr_t (来自stdint.h / inttypes.h )并使用相应的整数转换说明符。 这将允许更灵活地指示指针的格式,但严格来说,不需要实现来提供这些typedef。