正确的格式说明符来打印指针(地址)?
我应该使用哪种格式说明符来打印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_t
为uintptr_t
或intptr_t
(来自stdint.h
/ inttypes.h
),并使用相应的整数转换说明符。 这将在指针格式化方面提供更多的灵活性,但是严格地说,不需要实现提供这些types定义。