为什么以下输出?

时间:2013-04-12 08:01:14

标签: c visual-c++ gcc

我正在使用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

4 个答案:

答案 0 :(得分:1)

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

关于,

  

一个函数的值为2268303,另一个为2264159

注意到,一个char buf []在main()中声明,另一个在do()中。两者都有不同的存储空间,两者都可以在自己的范围内工作[阅读C中变量的范围]

答案 1 :(得分:1)

让我们分解一下,看看我们可以对每行代码说一遍,一个接一个:

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

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

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

数组类型的表达式,除非它是地址(&),sizeof_Alignof运算符的操作数,或者它是用于初始化字符数组的字符串文字,被转换为指向数组第一个元素的指针(6.3.2.1 p.3),因此该行等同于

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

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

(unsigned) buf 2268303

被打印出来了。下一步

    doing();

让我们看一下doing

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

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

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

同样,buf类型的char[4097]表达式转换为char*,即&buf[0],并传递给期望printf的{​​{1}} {1}}论点。类型不匹配调用未定义的行为,指针值的int字节通常被解释为有符号整数。结果是ouptut

sizeof(int)

强烈暗示buf 2264159 中的buf被分配了距离doing 4144个字节,并且堆栈向下增长。

main

我们再次进行了数组到指针的转换,现在 printf("buf %f\n", buf); 需要一个printf参数但得到double。更多未定义的行为,表现形式是

char*

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

printf

这一行与 printf("buf %d\n", (unsigned) buf); 的对应行具有相同的语义,但由于它是一个不同的数组main,因此从转换中获得的(无符号)整数是不同的。

buf

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

doing

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

buf[2]

被打印出来了。

buf 

printf("buf %d\n", buf+2); 再次评估为buf + 2,并在&buf[2]期望printf的位置传递。类型不匹配会调用未定义的行为,但输出

int

表示没有发生任何恶意,因为buf 2264161 &buf[2]落后两个字节,所打印的数字比&buf[0]首先doing中打印的数字大两倍

printf

返回}

main

该行与 printf("(unsigned) buf %d\n", (unsigned) buf); 中的第一个printf调用相同,因此它具有相同的语义,如上所述,

main

并产生相同的输出。

(unsigned) buf 2268303

答案 2 :(得分:0)

如果您使用%p,则会获得hex个值。 您还需要了解scope。也可以浏览these links

(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)

答案 3 :(得分:0)

以下是解释:

此:

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中,另一个在done()内)是两个不同的数组,即使您给它们指定了相同的名称。 在do()中,“buf”指的是你在那里创建的那个,在主“buf”里面也指你在那里创建的那个。这就是为什么从内部打印或主要打印时获得两个不同地址的原因