我知道这是一种危险的行为,我只是想知道发生了什么。
代码如下:
#include<stdio.h>
#include<stdlib.h>
static int count = 0;
void hello(void){
count ++;
fprintf(stderr,"hello! %d\n",count);
}
void foo(void){
void *buf[10];
static int i;
for(i = 0 ; i < 100 ; i++){ // put enough data to overwrite the return address on stack
buf[i] = hello;
}
}
int main(void){
int buf[1000];
foo();
return 0;
}
结果:
……
hello! 83
hello! 84
hello! 85
hello! 86
hello! 87
hello! 88
hello! 89
Segmentation fault (core dumped)
为什么hello func被称为89次? 当foo返回时,pc寄存器获取hello func的地址,不是吗? 所以你好叫,然后在里面做什么。那又怎样?该计划是否回归主要功能? 89来自哪里?我必须误解一些事情,请指出。
答案 0 :(得分:9)
您将函数hello
的地址写入foo
的堆栈,超过您使用buf
保留的空间量的90倍。当foo
试图返回时,它会将hello
的第一个额外地址(其中一个)加载到程序计数器中,弹出它,然后返回&#34;它。我之所以说&#34;其中一个&#34;是因为在您保留的数组的末尾和返回地址的开头之间,堆栈上可能有一些空间。
hello
假设它由foo
调用。但是,它没有被调用,换句话说,foo
的剩余部分的地址没有被推到堆栈上,hello
返回。因此,当hello
返回时,它弹出的东西是堆栈中的下一个东西,它又是hello
的地址。
这再次看起来就像hello
的观点,但它实际上并不是一个电话,因为它是一个回复,所以hello
不断回归自己并弹出每次从堆栈中获取hello
的一个地址,直到它用完这些地址。
在那些用完之后,堆栈上的下一个字节,当你被视为指针时,你没有被覆盖,它们被存储在内存中的其他地方,可能是未分配给你的进程内存映射的东西。 hello
试图回到那个,当程序发生错误时就是这样。
答案 1 :(得分:5)
这是未定义行为。当您进入未分配的内存任何都可能发生,具体取决于&#34;垃圾&#34;现在有。
分段错误基本上意味着你走出了程序的受保护内存(在你的情况下花了#34; 89次&#34;)。您的操作系统会对此进行保护,并告诉您您尝试在为该程序分配的空间之外编写。
(旧操作系统不是这种情况,如果程序溢出到系统空间,这样的错误可能会导致操作系统崩溃。)
答案 2 :(得分:1)
正如Nahum所说,这是未定义的行为,但这并不意味着它无法解释:
100 - 89 = 11
这正是本地堆栈的大小(void * [10] + int)。
尝试此操作(未定义的行为,可能会擦除您的硬盘或修复全球经济):
#include<stdio.h>
#include<stdlib.h>
static int count = 0;
static void hello(void){
count ++;
fprintf(stderr,"hello! %d\n",count);
}
static void foo(void){
void *buf[1];
static int i;
for(i = 0 ; i <= 100 + 2; i++){ // put enough data to overwrite the return address on stack
buf[i] = hello;
}
}
static void bye(void) {
printf("Bye, bye...\n");
exit(EXIT_SUCCESS);
}
int main(void){
void *buf[1000];
int i;
for (i = 0; i < 1000; ++i) {
buf[i] = &bye;
}
foo();
return 0;
}