为什么在不对变量进行任何修改的情况下,两个 printf 语句之间打印的指针变量地址不同?
Why the address of the pointer variable printed differently between two printf statements without any modification to the variable?
在下面的简单 c 测试程序中,两个 printf 语句 return 不同的值。 (检查最后四个 printf 语句)。
int main ()
{
char c, *cc;
int i;
long l;
float f;
c = 'Z';
i = 15;
l = 7777;
f = 9999999312;
cc = &c;
printf("\nc = %c, cc= %u", *cc, cc);
cc = &i;
printf("\nc = %d, cc= %u", *cc, cc);
cc = &l;
printf("\nc = %ld, cc= %u", *( long* )cc, cc);
printf("\nc = %ld, cc= %u", *cc, cc);
cc = &f;
printf("\nc = %f, cc= %u", *(float *)cc, cc);
printf("\n cc= %u", cc);
printf("\nc = %f, cc= %u", *cc, cc);
printf("\nc = %f, cc= %u", *(float *)cc, cc);
printf("\nc = %f, cc using pointer = %p", *(float *)cc, cc);
printf("\nc = %f, cc using pointer =%p", *cc, cc);
return 0;
}
输出:-
c = Z, cc= 755585903
c = 15, cc= 755585904
c = 7777, cc= 755585912
c = 97, cc= 755585912
c = 9999998976.000000, cc= 755585908
cc= 755585908
c = 9999998976.000000, cc= 4294967288
c = 9999998976.000000, cc= 755585908
c = 9999998976.000000, cc using pointer = 0x7ffc37f4ace4
c = 9999998976.000000, cc using pointer =0xfffffff8
我是 运行 它在 eclipse ide 中使用 Ubuntu Linux.
为什么它的行为不同?
在几个地方,您对 printf
使用了错误的格式说明符。特别是在这一行:
printf("\nc = %f, cc= %u", *cc, cc);
第一个参数的类型为 char
,但您使用的 %f
需要 double
。此外,第二个参数的类型为 char *
,但 %u
需要 unsigned int
。使用错误的格式说明符调用 undefined behavior.
话虽这么说,但最有可能发生的事情是:
在大多数托管实现中,浮点数不会像整数类型和指针那样被压入堆栈,而是存储在浮点寄存器中。
当你运行上面的printf
命令时,*cc
和cc
都被压入堆栈,因为它们都不是浮点数。当 printf
然后查找它看到的第一个参数时 %f
,因此它从浮点寄存器中检索一个值。由于您在之前调用 printf
时确实传递了一个浮点值,该值仍然存在,并且该值恰好是您实际想要打印的值,所以这就是打印的内容。
然后当 printf
打印下一个参数时,它在格式中看到 %u
因此从堆栈中提取值。该值是 *cc
,它指向 f
.
表示中的第一个字节
假设一个float
是一个IEEE754单精度浮点数,它包含的值表示为0xf8021550
。第一个字节是 0xf8
。由于 cc
指向 char
,在您的情况下它似乎已签名,因此被解释为负值。当传递给 printf
时,该值被提升为类型 int
,因此传入的实际值是 0xfffffff8
,这就是您看到的打印内容。
不过要重申一下,您看到的输出是未定义的行为。如果您在不同的机器上构建它,使用不同的编译器,或者只是使用不同的优化设置,输出可能会改变。
以下建议代码:
- 干净地编译
- 没有因不正确的分配和对
printf()
的错误调用而失败
- 直到程序退出,实际输出才会传递到终端,因为调用
printf()
的所有格式字符串都缺少尾随的 '\n'。
注意:建议的代码是在 64 位系统上使用以下命令行编译的:
gcc -ggdb -Wall -Wextra -Wconversion -std=gnu11 -pedantic -o "untitled2" "untitled2.c"
现在建议的代码:
#include <stdio.h>
int main ( void )
{
char c;
char *cc;
int i;
long l;
float f;
c = 'Z';
i = 15;
l = 7777;
f = 9999999312.0f;
cc = &c;
printf("\nc = %c, cc= %p", *cc, cc);
cc = (char*)&i;
printf("\nc = %d, cc= %p", *cc, cc);
cc = (char*)&l;
printf("\nc = %ld, cc= %p", *( long* )cc, cc);
printf("\nc = %ld, cc= %p", *(long*)cc, cc);
cc = (char*)&f;
printf("\nc = %f, cc= %p", *(float *)cc, cc);
printf("\n cc= %p", cc);
printf("\nc = %f, cc= %p", *(float*)cc, cc);
printf("\nc = %f, cc= %p", *(float*)cc, cc);
printf("\nc = %f, cc using pointer = %p", *(float *)cc, cc);
printf("\nc = %f, cc using pointer = %p", *(float*)cc, cc);
return 0;
}
结果输出为:
c = Z, cc= 0x7fffb663ae2f
c = 15, cc= 0x7fffb663ae30
c = 7777, cc= 0x7fffb663ae38
c = 7777, cc= 0x7fffb663ae38
c = 9999998976.000000, cc= 0x7fffb663ae34
cc= 0x7fffb663ae34
c = 9999998976.000000, cc= 0x7fffb663ae34
c = 9999998976.000000, cc= 0x7fffb663ae34
c = 9999998976.000000, cc using pointer = 0x7fffb663ae34
c = 9999998976.000000, cc using pointer = 0x7fffb663ae34
在下面的简单 c 测试程序中,两个 printf 语句 return 不同的值。 (检查最后四个 printf 语句)。
int main ()
{
char c, *cc;
int i;
long l;
float f;
c = 'Z';
i = 15;
l = 7777;
f = 9999999312;
cc = &c;
printf("\nc = %c, cc= %u", *cc, cc);
cc = &i;
printf("\nc = %d, cc= %u", *cc, cc);
cc = &l;
printf("\nc = %ld, cc= %u", *( long* )cc, cc);
printf("\nc = %ld, cc= %u", *cc, cc);
cc = &f;
printf("\nc = %f, cc= %u", *(float *)cc, cc);
printf("\n cc= %u", cc);
printf("\nc = %f, cc= %u", *cc, cc);
printf("\nc = %f, cc= %u", *(float *)cc, cc);
printf("\nc = %f, cc using pointer = %p", *(float *)cc, cc);
printf("\nc = %f, cc using pointer =%p", *cc, cc);
return 0;
}
输出:-
c = Z, cc= 755585903
c = 15, cc= 755585904
c = 7777, cc= 755585912
c = 97, cc= 755585912
c = 9999998976.000000, cc= 755585908
cc= 755585908
c = 9999998976.000000, cc= 4294967288
c = 9999998976.000000, cc= 755585908
c = 9999998976.000000, cc using pointer = 0x7ffc37f4ace4
c = 9999998976.000000, cc using pointer =0xfffffff8
我是 运行 它在 eclipse ide 中使用 Ubuntu Linux.
为什么它的行为不同?
在几个地方,您对 printf
使用了错误的格式说明符。特别是在这一行:
printf("\nc = %f, cc= %u", *cc, cc);
第一个参数的类型为 char
,但您使用的 %f
需要 double
。此外,第二个参数的类型为 char *
,但 %u
需要 unsigned int
。使用错误的格式说明符调用 undefined behavior.
话虽这么说,但最有可能发生的事情是:
在大多数托管实现中,浮点数不会像整数类型和指针那样被压入堆栈,而是存储在浮点寄存器中。
当你运行上面的printf
命令时,*cc
和cc
都被压入堆栈,因为它们都不是浮点数。当 printf
然后查找它看到的第一个参数时 %f
,因此它从浮点寄存器中检索一个值。由于您在之前调用 printf
时确实传递了一个浮点值,该值仍然存在,并且该值恰好是您实际想要打印的值,所以这就是打印的内容。
然后当 printf
打印下一个参数时,它在格式中看到 %u
因此从堆栈中提取值。该值是 *cc
,它指向 f
.
假设一个float
是一个IEEE754单精度浮点数,它包含的值表示为0xf8021550
。第一个字节是 0xf8
。由于 cc
指向 char
,在您的情况下它似乎已签名,因此被解释为负值。当传递给 printf
时,该值被提升为类型 int
,因此传入的实际值是 0xfffffff8
,这就是您看到的打印内容。
不过要重申一下,您看到的输出是未定义的行为。如果您在不同的机器上构建它,使用不同的编译器,或者只是使用不同的优化设置,输出可能会改变。
以下建议代码:
- 干净地编译
- 没有因不正确的分配和对
printf()
的错误调用而失败
- 直到程序退出,实际输出才会传递到终端,因为调用
printf()
的所有格式字符串都缺少尾随的 '\n'。
注意:建议的代码是在 64 位系统上使用以下命令行编译的:
gcc -ggdb -Wall -Wextra -Wconversion -std=gnu11 -pedantic -o "untitled2" "untitled2.c"
现在建议的代码:
#include <stdio.h>
int main ( void )
{
char c;
char *cc;
int i;
long l;
float f;
c = 'Z';
i = 15;
l = 7777;
f = 9999999312.0f;
cc = &c;
printf("\nc = %c, cc= %p", *cc, cc);
cc = (char*)&i;
printf("\nc = %d, cc= %p", *cc, cc);
cc = (char*)&l;
printf("\nc = %ld, cc= %p", *( long* )cc, cc);
printf("\nc = %ld, cc= %p", *(long*)cc, cc);
cc = (char*)&f;
printf("\nc = %f, cc= %p", *(float *)cc, cc);
printf("\n cc= %p", cc);
printf("\nc = %f, cc= %p", *(float*)cc, cc);
printf("\nc = %f, cc= %p", *(float*)cc, cc);
printf("\nc = %f, cc using pointer = %p", *(float *)cc, cc);
printf("\nc = %f, cc using pointer = %p", *(float*)cc, cc);
return 0;
}
结果输出为:
c = Z, cc= 0x7fffb663ae2f
c = 15, cc= 0x7fffb663ae30
c = 7777, cc= 0x7fffb663ae38
c = 7777, cc= 0x7fffb663ae38
c = 9999998976.000000, cc= 0x7fffb663ae34
cc= 0x7fffb663ae34
c = 9999998976.000000, cc= 0x7fffb663ae34
c = 9999998976.000000, cc= 0x7fffb663ae34
c = 9999998976.000000, cc using pointer = 0x7fffb663ae34
c = 9999998976.000000, cc using pointer = 0x7fffb663ae34