简体   繁体   English

为什么%p不显示指针的整个宽度?

[英]Why doesn't %p show the full width of pointer?

If I am not mistaken, on a 64bit machine, a pointer is equivalent to a number between 0 and 2^64-1 . 如果我没有弄错,在64位机器上,指针相当于02^64-1之间的数字。 Hence the following results: 因此得到以下结果:

printf("%p",    (void*)     -1); → 0xffffffffffffffff
printf("0x%lx", (uintptr_t) -1); → 0xffffffffffffffff

yet when I print a pointer from an object I just allocated, the results do not show the same width: 然而,当我从刚分配的对象打印指针时,结果显示的宽度不同:

printf("%p", (void*) &myobject); → 0x70d940

Is there any reason why all my objects are (always) given an adress at the begining of the memory space ? 是否有任何理由为什么我的所有对象(总是)在内存空间的开头给出一个地址? Thanks to virtual memory the could be placed (from the program's point of view) basicaly anywhere in the 64bit space. 由于虚拟内存,可以放置(从程序的角度来看)基本上在64位空间的任何地方。

Also why doesn't %p print the full width by default ? 另外为什么%p默认不打印全宽? (I would expect if to act like 0x%016lx ) (我希望如果像0x%016lx那样0x%016lx

Edit: Also, is there any reason why 0x018p is not valid ? 编辑:另外,有什么理由为什么0x018p无效?

error : fanion « 0 » used with « %p » gnu_printf format

As noted in a comment, 如评论中所述,

%p can format the address in any way it chooses. %p可以以其选择的任何方式格式化地址。 It doesn't have to be hex (though it usually is); 它不必是十六进制(虽然它通常是); the output doesn't have to start 0x or 0X ; 输出不必启动0x0X ; it might print hex in upper-case or lower-case. 它可能以大写或小写形式打印十六进制。

Try printf("%p\\n", (void *)0); 尝试printf("%p\\n", (void *)0); — on a Mac, that prints just 0 even though other values are printed 0x1234 style. - 在Mac上,即使其他值打印为0x1234样式,也只打印0 Try printf("%p\\n", (void *)1024); 尝试printf("%p\\n", (void *)1024); — it probably prints a short address 0x400 . - 它可能会打印一个短地址0x400

There are no requirements in the standards (POSIX or C or C++) specifying what the output from %p should look like. 标准(POSIX或C或C ++)中没有要求指定%p的输出应该是什么样的。

If you want control over the format of pointers, use uintptr_t and the PRIzPTR macros from <inttypes.h> : 如果要控制指针的格式,请使用uintptr_t<inttypes.h>PRIzPTR宏:

void *vp = 0;
printf("0x%.16" PRIXPTR " contains 0x%" PRIXPTR "\n",
       (uintptr_t)&vp, (uintptr_t)vp);

Or use PRIxPTR if you prefer lower-case hex digits. 如果您喜欢小写的十六进制数字,请使用PRIxPTR I don't, and I do like the 0x prefix, so that's what I use when I'm not constrained by someone else's questionable æsthetics. 我没有,而且我确实喜欢0x前缀,所以当我不受别人可疑的美学限制时,我就会使用它。

The exact representation of pointers via the %p format specifier is implementation defined. 通过%p格式说明符的指针的精确表示是实现定义的。

From section 7.21.6.1 of the C standard : C标准的第7.21.6.1节开始:

p The argument shall be a pointer to void . p参数应该是指向void的指针。 The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner. 指针的值以实现定义的方式转换为打印字符序列。

On Linux in particular, the man page states the following: 特别是在Linux上, 手册页说明了以下内容:

The void * pointer argument is printed in hexadecimal (as if by %#x or %#lx ) void * pointer参数以十六进制打印(就像%#x%#lx

So if you are on Linux, you might be able to get away with using %#016lx 因此, 如果您使用的是Linux,那么您可以使用%#016lx

However, that's just one implementation, so you can't assume anything about the format in a conforming C application. 但是,这只是一个实现,因此您无法假设符合C的应用程序中的格式。

For example, on Turbo C running on DOS, a pointer actually contains two values, a segment and an offset. 例如,在DOS上运行的Turbo C上,指针实际上包含两个值,一个段和一个偏移量。

Speaking mainly from a C perspective, since you've tagged [C] and you're using C-style code. 主要从C角度讲,因为你已经标记了[C]并且你正在使用C风格的代码。

If I am not mistaken, on a 64bit machine, a pointer is equivalent to a number between 0 and 2^64-1. 如果我没有弄错,在64位机器上,指针相当于0到2 ^ 64-1之间的数字。

Well that's a bit tricky. 那有点棘手。 Pointers can be converted to integers and back, but the size required for an integer type to be capable of supporting all possible round-trip pointer-integer-pointer conversions without data loss is unspecified. 指针可以转换为整数和返回,但是未指定整数类型所需的大小能够支持所有可能的往返指针 - 整数指针转换而不会丢失数据。 Implementations are not required to provide such a type. 实现不需要提供这样的类型。

Moreover, pointers are not required to be represented as integer indexes into a flat address space, and indeed, historically, some implementations have used different representations. 此外,指针不需要被表示为整数索引到平坦地址空间中,并且实际上,历史上,一些实现已经使用了不同的表示。 It is therefore not correct or safe to assert that a pointer is equivalent to a number, regardless of range. 因此,无论范围如何,断言指针等同于数字是不正确或安全的。

Note also that this ... 还要注意这个......

 printf("0x%lx", (uintptr_t) -1); 

... is unsafe, inasmuch as it assumes that uintptr_t is unsigned long int , which is by no means guaranteed to be the case. ...是不安全的,因为它假定uintptr_tunsigned long int ,但这绝不是保证的。 The safe way to write a format string for a value of type uintptr_t is: uintptr_t的值编写格式字符串的安全方法是:

printf("0x%" PRIxPTR, (uintptr_t) -1);

(Uses inttypes.h ) (使用inttypes.h

Is there any reason why all my objects are (always) given an adress at the begining of the memory space ? 是否有任何理由为什么我的所有对象(总是)在内存空间的开头给出一个地址? Thanks to virtual memory the could be placed (from the program's point of view) basicaly anywhere in the 64bit space. 由于虚拟内存,可以放置(从程序的角度来看)基本上在64位空间的任何地方。

This is well beyond anything specified by C or C++, but yes, on a virtual-memory machine, your objects could be placed anywhere in the system's virtual address space. 这远远超出了C或C ++指定的范围,但是,在虚拟内存机器上,您的对象可以放在系统的虚拟地址空间中的任何位置。 Why should they be, however? 但是他们为什么要这样呢? If your objects are dynamically allocated or have automatic duration then they are likely to be allocated in relatively consistent regions, because the system has regions that it uses for those purposes. 如果您的对象是动态分配的或具有自动持续时间,那么它们很可能在相对一致的区域中分配,因为系统具有用于这些目的的区域。 If the objects have static duration then they might, in principle, be subject to ASLR , but that depends on the system, the C implementation, compilation options, and other details. 如果对象具有静态持续时间,那么它们原则上可能受ASLR约束,但这取决于系统,C实现,编译选项和其他细节。

Also why doesn't %p print the full width by default ? 另外为什么%p默认不打印全宽? (I would expect if to act like 0x%016lx) (我希望如果像0x%016lx那样行事)

Why should it do so? 它为什么要这样做? Your expectation is unjustified. 你的期望是没有道理的。 printf() generally does not print leading zeroes unless you explicitly ask it to do so. printf()通常不会打印前导零,除非您明确要求它这样做。 C does not even require that the output be formatted as a hexadecimal number -- the format is completely implementation-defined. C甚至不要求将输出格式化为十六进制数 - 格式完全是实现定义的。

Edit: Also, is there any reason why 0x018p is not valid ? 编辑:另外,有什么理由为什么0x018p无效?

Because the standard explicitly says that the 0 flag character causes undefined behavior when used with the p conversion specifier. 因为标准明确指出0标志字符在与p转换说明符一起使用时会导致未定义的行为。 More generally, it produces UB when used with any conversion specifier other than d , i , o , u , x , X , a , A , e , E , f , F , g , or G . 更一般地,当与除diouxXaAeEfFgG之外的任何转换说明符一起使用时,它产生UB。 That's a long list of specifiers, but p is not on it. 这是一长串的说明符,但p不在其上。 If you look carefully, you'll see that it's all the ones that specify conversion to numeric format, which, again, C does not specify p to do (though some implementations may so specify). 如果你仔细观察,你会发现这一切都让指定转换为数字格式,其中,再次,C 没有指定的那些p做(尽管有些实现可以使指定)。

It is because in most C++ implementations the pointer printing (%p) suppresses leading zeroes to make things more readable. 这是因为在大多数C ++实现中,指针打印(%p)会抑制前导零以使事物更具可读性。 You can use formatting tricks while printing to get the full address width. 您可以在打印时使用格式化技巧来获取完整的地址宽度。

EDIT: The 'formatting trick' I mentioned: 编辑:我提到的'格式化技巧':

std::cout
<< "0x"
<< std::hex
<< std::noshowbase
<< std::setw(2)
<< std::setfill('0')
<< n
<< std::endl ;

Taken from https://stackoverflow.com/a/30326177/7407065 取自https://stackoverflow.com/a/30326177/7407065

Also why doesn't %p print the full width by default? 另外为什么%p默认不打印全宽?

Printing the full width of pointers by default breaks the style of printf() . 默认情况下,打印指针的整个宽度会破坏printf()的样式。 A "full width" option is available through modifiers. 通过修饰符可以使用“全宽”选项。

The various unmodified specifiers print minimal text. 各种未修改的说明符打印最小文本。

printf("%d", INT_MIN);
// "-2147483648"   example
printf("%d", 123);
// "123", not "+0000000123"

double value_nearest_one_seventh = 1/7.0;
printf("%f", value_nearest_one_seventh);
// "0.142857" 
// instead of its exact value of
// 0.142857142857142849212692681248881854116916656494140625

Should code need to print a pointer to a fixed width, use the length modifier . 如果代码需要打印指向固定宽度的指针,请使用长度修饰符

void *ptr = ptr;
int pointer_print_width = sizeof("0xffffffffffffffff") - 1;
printf("<%*p>", pointer_print_width, ptr);
// <          0x28cc6c>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM