我应该使用哪种格式说明符来打印变量的地址?我分不清下面这批货。
%u -无符号整数 %x -十六进制值 %p -空指针
打印地址的最佳格式是什么?
我应该使用哪种格式说明符来打印变量的地址?我分不清下面这批货。
%u -无符号整数 %x -十六进制值 %p -空指针
打印地址的最佳格式是什么?
当前回答
你可以使用%x或%x或%p;他们都是正确的。
如果使用%x,则地址以小写形式给出,例如:a3bfbc4 如果使用%X,则地址以大写形式给出,例如:A3BFBC4
这两个都是正确的。
如果您使用%x或%x,它将考虑地址的六个位置,如果您使用%p,它将考虑地址的八个位置。例如:
其他回答
作为其他(非常好的)答案的替代方案,你可以转换为uintptr_t或intptr_t(从stdint.h/inttypes.h)并使用相应的整数转换说明符。这将使指针的格式更加灵活,但严格来说,实现并不需要提供这些typedef。
假设您不介意不同平台之间格式的变化,最简单的答案是标准%p表示法。
C99标准(ISO/IEC 9899:1999)在§7.19.6.1中说¶8:
参数应该是指向void的指针。指针的值为 在实现定义中转换为打印字符序列 的方式。
(在C11 - ISO/IEC 9899:2011 -信息在§7.21.6.1¶8。)
在某些平台上,这将包括一个前导0x,而在其他平台上则不会,字母可以是小写或大写,C标准甚至没有定义它应该是十六进制输出,尽管我知道没有任何实现不是这样的。
It is somewhat open to debate whether you should explicitly convert the pointers with a (void *) cast. It is being explicit, which is usually good (so it is what I do), and the standard says 'the argument shall be a pointer to void'. On most machines, you would get away with omitting an explicit cast. However, it would matter on a machine where the bit representation of a char * address for a given memory location is different from the 'anything else pointer' address for the same memory location. This would be a word-addressed, instead of byte-addressed, machine. Such machines are not common (probably not available) these days, but the first machine I worked on after university was one such (ICL Perq).
如果你对%p的实现定义的行为不满意,那么使用C99 <inttypes.h>和uintptr_t代替:
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
This allows you to fine-tune the representation to suit yourself. I chose to have the hex digits in upper-case so that the number is uniformly the same height and the characteristic dip at the start of 0xA1B2CDEF appears thus, not like 0xa1b2cdef which dips up and down along the number too. Your choice though, within very broad limits. The (uintptr_t) cast is unambiguously recommended by GCC when it can read the format string at compile time. I think it is correct to request the cast, though I'm sure there are some who would ignore the warning and get away with it most of the time.
Kerrek在评论中问道:
我对标准推广和可变参数有点困惑。是否所有指针都被标准提升为void*?否则,如果int*是,比如说,两个字节,而void*是4个字节,那么从参数中读取四个字节显然是一个错误,不是吗?
我误以为C标准规定所有对象指针的大小必须相同,所以void *和int *的大小不能不同。然而,我认为C99标准的相关部分并不是那么强调(尽管我不知道有哪个实现是我建议的是真的,但实际上是假的):
§6.2.5 Types ¶26 A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.39) Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements. 39) The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.
(C11在§6.2.5节、¶28和脚注48中说的完全相同。)
So, all pointers to structures must be the same size as each other, and must share the same alignment requirements, even though the structures the pointers point at may have different alignment requirements. Similarly for unions. Character pointers and void pointers must have the same size and alignment requirements. Pointers to variations on int (meaning unsigned int and signed int) must have the same size and alignment requirements as each other; similarly for other types. But the C standard doesn't formally say that sizeof(int *) == sizeof(void *). Oh well, SO is good for making you inspect your assumptions.
C标准明确不要求函数指针与对象指针大小相同。为了不破坏类dos系统上的不同内存模型,这样做是必要的。你可以有16位数据指针,但32位函数指针,反之亦然。这就是为什么C标准没有规定函数指针可以转换为对象指针,反之亦然。
幸运的是(对于以POSIX为目标的程序员),POSIX打破了这一规则,要求函数指针和数据指针的大小相同:
§2.12.3指针类型 所有函数指针类型都应与指向void的类型指针具有相同的表示形式。将函数指针转换为void *不会改变其表示形式。由这种转换产生的void *值可以使用显式强制转换转换回原始的函数指针类型,而不会丢失信息。 注意: ISO C标准不要求这样做,但是POSIX一致性要求这样做。
因此,在将指针传递给可变变量函数(如printf())时,显式强制转换为void *似乎是非常可取的,以提高代码的可靠性。在POSIX系统上,将函数指针强制转换为空指针进行打印是安全的。在其他系统上,这样做不一定安全,传递void *以外没有强制转换的指针也不一定安全。
P是打印指针的转换说明符。用这个。
int a = 42;
printf("%p\n", (void *) &a);
请记住,省略强制转换是未定义的行为,使用p转换说明符的打印是以实现定义的方式完成的。
使用%p表示“指针”,不要使用其他任何符号*。标准不能保证你可以像对待任何特定类型的整数一样对待指针,所以你实际上会得到积分格式的未定义行为。(例如,%u期望无符号整型,但如果void*的大小或对齐要求与无符号整型不同怎么办?)
参见乔纳森的精彩回答!除了%p,你还可以使用C99中添加的<inttypes.h>中的指针特定宏。
在C语言中,所有对象指针都可以隐式转换为void*,但为了将指针作为可变参数传递,必须显式强制转换它(因为任意对象指针只能转换,但不等同于void指针):
printf("x lives at %p.\n", (void*)&x);
你可以使用%x或%x或%p;他们都是正确的。
如果使用%x,则地址以小写形式给出,例如:a3bfbc4 如果使用%X,则地址以大写形式给出,例如:A3BFBC4
这两个都是正确的。
如果您使用%x或%x,它将考虑地址的六个位置,如果您使用%p,它将考虑地址的八个位置。例如: