正确的格式说明符来打印指针(地址)?

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

%u – 无符号整数

%x – hex值

%p – void指针

这将是打印地址的最佳格式?

最简单的答案,假设你不介意不同平台之间格式的变幻莫测和变化,就是标准的%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 <inttypes.h>uintptr_t

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

这使您可以微调表示以适应自己。 我select了大写的hex数字,使得数字的高度一致, 0xA1B2CDEF出现0xA1B2CDEF的开始处的特征凹陷,因此不像0xa1b2cdef那样沿着数字上下0xa1b2cdef 。 你的select虽然在很宽的范围内。 (uintptr_t)在GCC编译时可以读取格式string。 我认为要求演员是正确的,尽pipe我确信有些人会忽略警告,并且大部分时间都会放弃。


Kerrek在评论中问道:

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

我是在幻想C标准说所有对象指针必须是相同的大小,所以void *int *不能是不同的大小。 但是,我认为C99标准的相关部分并不是很有说服力(尽pipe我不知道我所说的是否是真实的实际上是错误的):

§6.2.5types

¶26指向void的指针与指向字符types的指针具有相同的表示和alignment要求。 同样,指向兼容types的合格或不合格版本的指针应具有相同的表示和alignment要求。 所有指向结构types的指针应该具有相同的表示和alignment要求。 所有指向联合types的指针应具有相同的表示和alignment要求。 指向其他types的指针不需要具有相同的表示或alignment要求。

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

(C11在§6.2.5,¶28和脚注48中也完全一样)

因此,所有指向结构的指针必须是相同的大小,并且必须共享相同的alignment要求,即使指针指向的结构可能有不同的alignment要求。 同样的工会。 字符指针和空指针必须具有相同的大小和alignment要求。 指向int (即unsigned int和有signed int )变体的指针必须具有相同的大小和alignment要求; 类似的其他types。 但是C标准并没有正式说sizeof(int *) == sizeof(void *) 。 噢,好吧,让你检查你的假设。

C标准明确地不要求函数指针与对象指针的大小相同。 这是有必要的,不要打破DOS类系统上的不同内存模型。 在那里你可以有16位的数据指针,但是32位的函数指针,反之亦然。 这就是为什么C标准没有规定函数指针可以转换为对象指针,反之亦然。

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

§2.12.3 指针types

所有的函数指针types都应该和指向void的types指针具有相同的表示forms。 将函数指针转换为void *不应改变表示forms。 由这种转换产生的void *值可以使用明确的转换转换回原始的函数指针types,而不会丢失信息。

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

所以,在将指针传递给可变参数函数(如printf()时,似乎强制转换为void *强烈build议在代码中实现最大可靠性。 在POSIX系统上,将函数指针强制转换为虚拟指针进行打印是安全的。 在其他系统上,这样做不一定是安全的,也没有必要通过除void *其他指针。

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

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

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

使用%p作为“指针”,不要使用其他的*。 你不能保证你可以像任何特定types的整数那样处理一个指针,所以你实际上会用整数格式得到未定义的行为。 (例如, %u期望unsigned int ,但是如果void*unsigned int具有不同的大小或alignment要求?)

*)[请参阅Jonathan的正确答案]除了%p ,还可以使用<inttypes.h>指针特定的macros,在C99中添加。

所有对象指针都可以隐式转换为C中的void* ,但为了将指针作为可变parameter passing,必须显式强制转换 (因为任意对象指针只能转换 ,而不能 void指针相同 ):

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

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