为什么在不对变量进行任何修改的情况下,两个 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命令时,*cccc都被压入堆栈,因为它们都不是浮点数。当 printf 然后查找它看到的第一个参数时 %f,因此它从浮点寄存器中检索一个值。由于您在之前调用 printf 时确实传递了一个浮点值,该值仍然存在,并且该值恰好是您实际想要打印的值,所以这就是打印的内容。

然后当 printf 打印下一个参数时,它在格式中看到 %u 因此从堆栈中提取值。该值是 *cc,它指向 f.

表示中的第一个字节

假设一个float是一个IEEE754单精度浮点数,它包含的值表示为0xf8021550。第一个字节是 0xf8。由于 cc 指向 char,在您的情况下它似乎已签名,因此被解释为负值。当传递给 printf 时,该值被提升为类型 int,因此传入的实际值是 0xfffffff8,这就是您看到的打印内容。

不过要重申一下,您看到的输出是未定义的行为。如果您在不同的机器上构建它,使用不同的编译器,或者只是使用不同的优化设置,输出可能会改变。

以下建议代码:

  1. 干净地编译
  2. 没有因不正确的分配和对 printf()
  3. 的错误调用而失败
  4. 直到程序退出,实际输出才会传递到终端,因为调用 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