【问题标题】:why the following output?为什么下面的输出?
【发布时间】:2013-04-04 16:02:19
【问题描述】:

我正在使用 gcc 并编写了一个小程序如下:

#include<stdio.h>

void doing(){
    char buf[4096 + 1];
    printf("buf %d\n", buf);
    printf("buf %f\n", buf);
    printf("buf %d\n", (unsigned) buf);
    printf("buf %s\n", buf+2);
    printf("buf %d\n", buf+2);

}


int main (void){
    char buf[4096 + 1];
    printf("(unsigned) buf %d\n", (unsigned) buf);
    doing();
    printf("(unsigned) buf %d\n", (unsigned) buf);
    return 0;
}

程序的输出是:

(unsigned) buf 2268303
buf 2264159
buf 0.000000
buf 2264159
buf
buf 2264161
(unsigned) buf 2268303

我不知道为什么将 2268303 打印为整数,以及这个 2268303 的值是从哪里来的,一个函数给出的值是 2268303,另一个是 2264159

【问题讨论】:

  • 你期待什么输出?
  • @icepack 期待(空)
  • 你为什么期待(null)
  • 你到底为什么会这样?
  • 因为不使用指针

标签: c visual-c++ gcc


【解决方案1】:

让我们把它分解一下,看看我们可以对每一行代码说些什么,一个接一个:

int main (void){
    char buf[4096 + 1];

您声明了一个char[4097],它通常通过在输入main 时调整堆栈指针来分配到堆栈上。数组的内容是不确定的。

    printf("(unsigned) buf %d\n", (unsigned) buf);

数组类型的表达式,除非它是地址(&amp;)、sizeof_Alignof 运算符的操作数,或者是用于初始化字符数组的字符串字面量,否则将转换为指针到数组的第一个元素(6.3.2.1 p. 3),因此该行相当于

    printf("(unsigned) buf %d\n", (unsigned) &buf[0]);

获取buf 中第一个字节的地址,将其转换为unsigned 并将结果数字打印为有符号整数。请注意,如果生成的 unsigned 值不能表示为 int,则行为未定义。指针 &amp;buf[0]unsigned 的转换是实现定义的,并且可能会调用未定义的行为 (6.3.2.3 p. 6)。通常,指针值的sizeof(unsigned) 字节被解释为无符号整数(通常包括指针的大小不小于unsigned 的大小)。在你的情况下,结果是

(unsigned) buf 2268303

已打印。下一个

    doing();

那么让我们看看doing

void doing(){
    char buf[4096 + 1];

声明了另一个char[4097],通常在进入doing 时通过调整堆栈指针来分配它。这个数组的内容也是不确定的。

    printf("buf %d\n", buf);

同样,char[4097] 类型的表达式buf 被转换为char*,即&amp;buf[0],然后传递给printf,它需要int 参数。类型不匹配会调用未定义的行为,通常sizeof(int) 指针值的字节被解释为有符号整数。结果是输出

buf 2264159

这强烈暗示doing 中的buf 被分配到距离main 4144 字节的位置,并且堆栈向下增长。

    printf("buf %f\n", buf);

我们再次进行了数组到指针的转换,现在printf 需要一个double 参数,但得到一个char*。更多未定义的行为,表现就是

buf 0.000000

被打印出来。一般无法回答这个问题(毕竟,这是未定义的行为),在 64 位系统上,常见行为是指针或整数类型的 printf 参数在通用寄存器中传递,浮点参数在浮点点寄存器,以便printf 会读取浮点寄存器 - 它恰好包含一个 0 值。

    printf("buf %d\n", (unsigned) buf);

这一行与main对应的行语义相同,但由于是不同的数组buf,所以转换得到的(无符号)整数是不同的。

buf 2264159

它与doing 中的第一个printf 打印相同,这并不奇怪(但不能保证,因为涉及未定义的行为)。

    printf("buf %s\n", buf+2);

buf 被转换为&amp;buf[0],然后加上 2,得到&amp;buf[2]。这被传递给printf,由于%s 转换需要一个指向char 的0 终止数组的指针作为参数。这是整个程序中唯一的printf 调用,其中printf 的第二个参数的类型与转换说明符所期望的类型完全匹配。但是buf的内容是不确定的,所以如果数组中没有0字节,就会导致printf读取无效。但是,显然buf[2] 是 0,所以只是

buf 

已打印。

    printf("buf %d\n", buf+2);

buf + 2 再次计算为&amp;buf[2],并且在printf 期望int 的地方通过。类型不匹配会调用未定义的行为,但输出

buf 2264161

表明没有发生任何恶意事件,并且由于&amp;buf[2]&amp;buf[0] 落后两个字节,因此打印的数字比doing 的第一个printf 中打印的数字大两个。

}

返回main

    printf("(unsigned) buf %d\n", (unsigned) buf);

该行与main 中的第一个printf 调用相同,因此具有与上面讨论的相同的语义,

(unsigned) buf 2268303

并产生相同的输出。

    return 0;
}

【讨论】:

    【解决方案2】:

    请阅读 C 基础知识。你会得到答案的。

    关于,

    一个函数给出的值是 2268303,另一个是 2264159

    请注意,一个char buf[] 在main() 中声明,另一个在doing() 中。两者都有不同的存储空间,并且都将在各自的范围内工作。 [读取 C 中变量的作用域]

    【讨论】:

      【解决方案3】:

      如果您使用%p,您将获得hex 值。 您还需要更新scope。也通过theselinks

      (unsigned) buf 2268303 // You have defined this in main its a local array so it has                      
                             //2268303 as address. And for an array the array name acts as a 
                             //pointer too
      buf 2264159            // You have defined this in fn doing, it is local to that function     
                             //and has a different address (it is a different array than the one 
                             //in your main)
      

      【讨论】:

        【解决方案4】:

        解释如下:

        这个:

        printf("buf %d\n", buf);
        

        将打印“buf”数组中第一个元素的地址(内存位置)

        这个:

        printf("buf %f\n", buf);
        

        将尝试从“buf”数组的第一个元素的地址读取浮点数,printf 可能返回零,因为它不知道如何处理它。

        这个:

        printf("buf %d\n", (unsigned) buf);
        

        将与第一个 printf 执行相同的操作,因为“buf”的地址始终大于或等于零。

        这个:

        printf("buf %s\n", buf+2);
        

        将尝试从“buf”数组中第一个元素的地址读取浮点数,printf 可能会返回一个空字符串,因为它不知道如何处理它。

        最后:

        printf("buf %d\n", buf+2);
        

        返回“buf”数组中第三个元素的位置

        重要提示: 您声明的两个“buf”数组(一个在 main 中,另一个在 do() 中)是两个不同的数组,即使您给它们提供了相同的名称。 在doing() 里面的“buf”指的是你在那里创建的那个,在main 里面的“buf”也指的是你在那里创建的那个。这就是为什么从内部做或主打印时得到两个不同地址的原因

        【讨论】:

        • printf("buf %f\n", buf); 会将buf 的地址解释为double,即8 个字节。
        • printf("buf %s\n", buf+2); 将尝试读取从地址buf + 2 开始的字符串,并且那里似乎只有零。
        猜你喜欢
        • 1970-01-01
        • 2022-11-16
        • 2023-01-20
        • 1970-01-01
        • 1970-01-01
        • 2019-05-08
        • 2022-11-27
        • 2021-05-08
        • 1970-01-01
        相关资源
        最近更新 更多