让我们把它分解一下,看看我们可以对每一行代码说些什么,一个接一个:
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 p. 6)。通常,sizeof(unsigned)指针值的字节被解释为无符号整数(通常包括指针的大小不小于 的大小unsigned)。在你的情况下,结果是
(unsigned) buf 2268303
被打印出来了。下一个
doing();
那么让我们看看doing:
void doing(){
char buf[4096 + 1];
另一个char[4097]是声明的,通常是通过在doing进入时调整堆栈指针来分配的。这个数组的内容也是不确定的。
printf("buf %d\n", buf);
buf同样, type的表达式char[4097]被转换为 a char*,即&buf[0],并且传递给printfwhich 需要一个int参数。类型不匹配会调用未定义的行为,通常sizeof(int)指针值的字节被解释为有符号整数。结果是输出
buf 2264159
这强烈暗示bufindoing已分配 4144 字节远离main's 并且堆栈向下增长。
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
它打印与第一个printfin相同doing,这并不奇怪(但不能保证,因为涉及未定义的行为)。
printf("buf %s\n", buf+2);
buf转换为&buf[0],然后将 2 添加到其中,得到&buf[2]. 这被传递给printf,由于%s转换,它需要一个指向char作为参数的以 0 结尾的数组的指针。这是printf整个程序中唯一的调用,其中printf第二个参数的类型与转换说明符所期望的类型完全匹配。但是 的内容buf是不确定的,所以如果数组中没有 0 字节,就会导致printf. 然而,显然buf[2]是 0,所以只是
buf
被打印出来了。
printf("buf %d\n", buf+2);
buf + 2再次评估为,并且在期望的&buf[2]地方传递。类型不匹配会调用未定义的行为,但输出printfint
buf 2264161
表明没有发生任何恶意事件,并且由于&buf[2]落后 两个字节&buf[0],因此打印的数字比doing's first中打印的数字大两个printf。
}
返回main:
printf("(unsigned) buf %d\n", (unsigned) buf);
该行与第一个printf调用相同main,因此它具有相同的语义,如上所述,
(unsigned) buf 2268303
并产生相同的输出。
return 0;
}