In a recent question, someone mentioned that when printing a pointer value with printf, the caller must cast the pointer to void *, like so:
在最近的一个问题中,有人提到当用printf打印指针值时,调用者必须将指针转换为void *,如下所示:
int *my_ptr = ....
printf("My pointer is: %p", (void *)my_ptr);
For the life of me I can't figure out why. I found this question, which is almost the same. The answer to question is correct - it explains that ints and pointers are not necessarily the same length.
我这辈子都搞不懂为什么。我发现了这个问题,几乎是一样的。问题的答案是正确的——它解释了ints和指针不一定是相同的长度。
This is, of course, true, but when I already have a pointer, like in the case above, why should I cast from int *
to void *
? When is an int * different from a void *? In fact, when does (void *)my_ptr
generate any machine code that's different from simply my_ptr
?
当然,这是正确的,但是当我已经有了一个指针,就像上面的例子一样,为什么我要从int *到void *?什么时候int与void *不同?实际上,什么时候(void *)my_ptr生成任何与my_ptr不同的机器代码?
UPDATE: Multiple knowledgeable responders quoted the standard, saying passing the wrong type may result in undefined behavior. How? I expect printf("%p", (int *)ptr)
and printf("%p", (void *)ptr)
to generate the exact same stack-frame. When will the two calls generate different stack frames?
更新:多个知情的响应人员引用了标准,说传递错误的类型可能导致未定义的行为。如何?我期望printf(“%p”,(int *)ptr)和printf(“%p”,(void *)ptr)生成完全相同的堆栈帧。这两个调用什么时候会生成不同的堆栈帧?
5 个解决方案
#1
13
In C language all pointer types potentially differ in their representations. So, yes, int *
is different from void *
. A real-life platform that would illustrate this difference might be difficult (or impossible) to find, but at the conceptual level the difference is still there.
在C语言中,所有指针类型在它们的表示形式中都可能存在差异。所以,是的,int *不同于void *。一个真实的平台来说明这种差异可能很难(或不可能)找到,但在概念层面上,这种差异仍然存在。
In other words, in general case different pointer types have different representations. int *
is different from void *
and different from double *
. The fact that your platform uses the same representation for void *
and int *
is nothing more than a coincidence, as far as C language is concerned.
换句话说,在一般情况下,不同的指针类型具有不同的表示形式。int *与void *不同,与double *不同。就C语言而言,您的平台对void *和int *使用相同的表示只是一个巧合。
The language states that some pointer types are required to have identical representations, which includes void *
vs. char *
, pointers to different struct types or, say, int *
and const int *
. But these are just exceptions from the general rule.
该语言声明某些指针类型必须具有相同的表示,包括void *和char *,指向不同结构类型的指针,或者,int *和const int *。但这些只是一般规则的例外。
#2
17
p
conversion specifier requires an argument of type void *
. If you don't pass an argument of type void *
, the function call invokes undefined behavior.
转换说明符需要一个类型为void *的参数。如果不传递类型为void *的参数,函数调用将调用未定义的行为。
From the C Standard:
从C标准:
(C11, 7.21.6.1p8 Formatted input/output functions) "p The argument shall be a pointer to void."
(C11, 7.21.6.1p8格式化的输入/输出函数)p论点应该是指向无效的指针。
Pointer types in C are not required to have the same size or the same representation.
C中的指针类型不需要具有相同的大小或相同的表示。
An example of an implementation with different pointer types representation is Cray PVP where the representation of pointer types is 64-bit for void *
and char *
but 32-bit for the other pointer types.
一个具有不同指针类型表示的实现示例是Cray PVP,其中void *和char *的指针类型表示为64位,而其他指针类型为32位。
See "Cray C/C++ Reference Manual", Table 3. in "9.1.2.2" http://docs.cray.com/books/004-2179-003/004-2179-003-manual.pdf
参见“Cray C/ c++参考手册”,表3。在“9.1.2.2”http://docs.cray.com/books/004 - 2179 003/004 - 2179 - 003 - manual.pdf
#3
3
In reality except on ancient mainframes/minis, different pointer types are extremely unlikely to have different sizes. However they have different types, and per the specification for printf
, calling it with the wrong type argument for the format specifier results in undefined behavior. This means don't do it.
实际上,除了在古老的大型机/minis上,不同的指针类型不太可能有不同的大小。但是它们有不同的类型,根据printf的规范,使用格式说明符的错误类型参数调用它会导致未定义的行为。这意味着不要这样做。
#4
3
c11: 7.21.6 Formatted input/output functions (p8):
p
The argument shall be a pointer tovoid
. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner.p这个参数应该是一个指向void的指针。指针的值以实现定义的方式转换为打印字符序列。
#5
3
Other people have adequately addressed the case of passing an int *
to a prototyped function with a fixed number of arguments that expects a different pointer type.
其他人已经充分地讨论了将int *传递给原型函数的情况,该函数具有一定数量的参数,这些参数要求使用不同的指针类型。
printf
is not such a function. It is a variadic function, so the default argument promotions are used for its anonymous arguments (i.e. everything after the format string) and if the promoted type of each argument does not exactly match the type expected by the format effector, the behavior is undefined. In particular, even if int *
and void *
have identical representation,
printf不是这样一个函数。它是一个可变的函数,因此默认的参数升级用于它的匿名参数(例如,格式字符串后面的所有参数),如果每个参数的提升类型与格式执行器所期望的类型不完全匹配,则行为未定义。特别是,即使int *和void *具有相同的表示,
int a;
printf("%p\n", &a);
has undefined behavior.
未定义的行为。
This is because the layout of the call frame may depend on the exact concrete type of each argument. ABIs that specify different argument areas for pointer and non-pointer types have occurred in real life (e.g. the Motorola 68000 would like you to keep pointers in the address registers and non-pointers in the data registers to the maximum extent possible). I'm not aware of any real-world ABI that segregates distinct pointer types, but it's allowed and it would not surprise me to hear of one.
这是因为调用框架的布局可能取决于每个参数的具体类型。为指针和非指针类型指定不同的参数区域的ABIs在现实生活中已经发生(例如,Motorola 68000希望您尽可能将指针保存在地址寄存器和数据寄存器中的非指针)。我不知道有任何真实世界的ABI可以隔离不同的指针类型,但是它是允许的,听到这样的情况我也不会感到惊讶。
#1
13
In C language all pointer types potentially differ in their representations. So, yes, int *
is different from void *
. A real-life platform that would illustrate this difference might be difficult (or impossible) to find, but at the conceptual level the difference is still there.
在C语言中,所有指针类型在它们的表示形式中都可能存在差异。所以,是的,int *不同于void *。一个真实的平台来说明这种差异可能很难(或不可能)找到,但在概念层面上,这种差异仍然存在。
In other words, in general case different pointer types have different representations. int *
is different from void *
and different from double *
. The fact that your platform uses the same representation for void *
and int *
is nothing more than a coincidence, as far as C language is concerned.
换句话说,在一般情况下,不同的指针类型具有不同的表示形式。int *与void *不同,与double *不同。就C语言而言,您的平台对void *和int *使用相同的表示只是一个巧合。
The language states that some pointer types are required to have identical representations, which includes void *
vs. char *
, pointers to different struct types or, say, int *
and const int *
. But these are just exceptions from the general rule.
该语言声明某些指针类型必须具有相同的表示,包括void *和char *,指向不同结构类型的指针,或者,int *和const int *。但这些只是一般规则的例外。
#2
17
p
conversion specifier requires an argument of type void *
. If you don't pass an argument of type void *
, the function call invokes undefined behavior.
转换说明符需要一个类型为void *的参数。如果不传递类型为void *的参数,函数调用将调用未定义的行为。
From the C Standard:
从C标准:
(C11, 7.21.6.1p8 Formatted input/output functions) "p The argument shall be a pointer to void."
(C11, 7.21.6.1p8格式化的输入/输出函数)p论点应该是指向无效的指针。
Pointer types in C are not required to have the same size or the same representation.
C中的指针类型不需要具有相同的大小或相同的表示。
An example of an implementation with different pointer types representation is Cray PVP where the representation of pointer types is 64-bit for void *
and char *
but 32-bit for the other pointer types.
一个具有不同指针类型表示的实现示例是Cray PVP,其中void *和char *的指针类型表示为64位,而其他指针类型为32位。
See "Cray C/C++ Reference Manual", Table 3. in "9.1.2.2" http://docs.cray.com/books/004-2179-003/004-2179-003-manual.pdf
参见“Cray C/ c++参考手册”,表3。在“9.1.2.2”http://docs.cray.com/books/004 - 2179 003/004 - 2179 - 003 - manual.pdf
#3
3
In reality except on ancient mainframes/minis, different pointer types are extremely unlikely to have different sizes. However they have different types, and per the specification for printf
, calling it with the wrong type argument for the format specifier results in undefined behavior. This means don't do it.
实际上,除了在古老的大型机/minis上,不同的指针类型不太可能有不同的大小。但是它们有不同的类型,根据printf的规范,使用格式说明符的错误类型参数调用它会导致未定义的行为。这意味着不要这样做。
#4
3
c11: 7.21.6 Formatted input/output functions (p8):
p
The argument shall be a pointer tovoid
. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner.p这个参数应该是一个指向void的指针。指针的值以实现定义的方式转换为打印字符序列。
#5
3
Other people have adequately addressed the case of passing an int *
to a prototyped function with a fixed number of arguments that expects a different pointer type.
其他人已经充分地讨论了将int *传递给原型函数的情况,该函数具有一定数量的参数,这些参数要求使用不同的指针类型。
printf
is not such a function. It is a variadic function, so the default argument promotions are used for its anonymous arguments (i.e. everything after the format string) and if the promoted type of each argument does not exactly match the type expected by the format effector, the behavior is undefined. In particular, even if int *
and void *
have identical representation,
printf不是这样一个函数。它是一个可变的函数,因此默认的参数升级用于它的匿名参数(例如,格式字符串后面的所有参数),如果每个参数的提升类型与格式执行器所期望的类型不完全匹配,则行为未定义。特别是,即使int *和void *具有相同的表示,
int a;
printf("%p\n", &a);
has undefined behavior.
未定义的行为。
This is because the layout of the call frame may depend on the exact concrete type of each argument. ABIs that specify different argument areas for pointer and non-pointer types have occurred in real life (e.g. the Motorola 68000 would like you to keep pointers in the address registers and non-pointers in the data registers to the maximum extent possible). I'm not aware of any real-world ABI that segregates distinct pointer types, but it's allowed and it would not surprise me to hear of one.
这是因为调用框架的布局可能取决于每个参数的具体类型。为指针和非指针类型指定不同的参数区域的ABIs在现实生活中已经发生(例如,Motorola 68000希望您尽可能将指针保存在地址寄存器和数据寄存器中的非指针)。我不知道有任何真实世界的ABI可以隔离不同的指针类型,但是它是允许的,听到这样的情况我也不会感到惊讶。