为什么指针变量的地址在两个printf语句之间不同地打印而不对变量进行任何修改?

时间:2018-03-12 14:19:28

标签: c linux memory memory-address

在下面的简单c测试程序中,两个printf语句返回不同的值。 (查看最后四个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。

为什么表现不同?

2 个答案:

答案 0 :(得分:3)

在某些地方,您使用了错误的格式说明符printf。特别是,在这一行:

printf("\nc = %f, cc= %u", *cc, cc);

第一个参数的类型为char,但您使用期望%f的{​​{1}}。此外,第二个参数的类型为double,但char *期待%u。使用错误的格式说明符会调用undefined behavior

话虽如此,这是最有可能发生在幕后的事情:

在大多数托管实现中,浮点数不会像整数类型和指针那样被压入堆栈,而是存储在浮点寄存器中。

当您运行上述unsigned int命令时,printf*cc都被压入堆栈,因为它们都不是浮点数。当cc然后查找它看到printf的第一个参数时,它会从浮点寄存器中检索一个值。由于您确实传递了一个浮点值,因此您之前调用%f时该值仍然存在,并且该值恰好是您实际想要打印的值,因此打印的内容就是<。 / p>

然后当printf去打印下一个参数时,它会看到格式为printf,以便从堆栈中提取值。该值为%u,它指向*cc表示中的第一个字节。

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

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

答案 1 :(得分:0)

以下提议的代码:

  1. 干净地编译
  2. 没有因错误分配和错误调用printf()
  3. 而失败
  4. 在程序退出之前,实际输出不会传递到终端,因为拨打printf()的所有格式字符串都缺少跟踪&#39; \ n&#39;。
  5. 注意:建议的代码是在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