vpr*_*jan 16 c stack callstack
为什么下面的代码没有任何崩溃@ runtime?
而且尺寸完全取决于机器/平台/编译器!! 我甚至可以在64位机器上放弃200.如何在OS中检测到主函数中的分段错误?
int main(int argc, char* argv[])
{
int arr[3];
arr[4] = 99;
}
Run Code Online (Sandbox Code Playgroud)
这个缓冲空间来自哪里?这是分配给进程的堆栈吗?
Fre*_*ihl 75
我之前写的一些用于教育目的的东西......
考虑以下c程序:
int q[200];
main(void) {
int i;
for(i=0;i<2000;i++) {
q[i]=i;
}
}
Run Code Online (Sandbox Code Playgroud)
在编译并执行它之后,会生成核心转储:
$ gcc -ggdb3 segfault.c
$ ulimit -c unlimited
$ ./a.out
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)
现在使用gdb执行事后分析:
$ gdb -q ./a.out core
Program terminated with signal 11, Segmentation fault.
[New process 7221]
#0 0x080483b4 in main () at s.c:8
8 q[i]=i;
(gdb) p i
$1 = 1008
(gdb)
Run Code Online (Sandbox Code Playgroud)
呵呵,当一个人在分配的200个项目之外写的时候,程序没有发生段错误,而是当i = 1008时崩溃了,为什么呢?
输入页面.
可以在UNIX/Linux上以多种方式确定页面大小,一种方法是使用系统函数sysconf(),如下所示:
#include <stdio.h>
#include <unistd.h> // sysconf(3)
int main(void) {
printf("The page size for this system is %ld bytes.\n",
sysconf(_SC_PAGESIZE));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它给出了输出:
该系统的页面大小为4096字节.
或者可以使用命令行实用程序getconf,如下所示:
$ getconf PAGESIZE
4096
Run Code Online (Sandbox Code Playgroud)
验尸
事实证明,segfault不是在i = 200时发生,而在i = 1008时,让我们找出原因.启动gdb进行一些事后分析:
$gdb -q ./a.out core
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
[New process 4605]
#0 0x080483b4 in main () at seg.c:6
6 q[i]=i;
(gdb) p i
$1 = 1008
(gdb) p &q
$2 = (int (*)[200]) 0x804a040
(gdb) p &q[199]
$3 = (int *) 0x804a35c
Run Code Online (Sandbox Code Playgroud)
q结束于地址0x804a35c,或者更确切地说,q [199]的最后一个字节位于该位置.页面大小与我们之前看到的4096字节相同,机器的32位字大小使虚拟地址分解为20位页码和12位偏移量.
q []以虚拟页码结尾:
0x804a = 32842偏移量:
0x35c = 860所以仍有:
4096 - 864 =在分配q []的内存页面上剩余3232个字节.这个空间可以容纳:
3232/4 = 808个整数,代码将其视为包含位置200到1008的q元素.
我们都知道这些元素不存在,编译器没有抱怨,因为我们对该页面有写权限,所以hw也没有.只有当i = 1008时,q []指的是我们没有写入权限的不同页面上的地址,虚拟内存hw检测到这一点并触发了段错误.
整数存储在4个字节中,这意味着该页面包含808(3236/4)个额外的伪元素,这意味着从q [200],q [201]一直到元素199访问这些元素仍然是完全合法的. + 808 = 1007(q [1007]),不触发seg故障.访问q [1008]时,您输入权限不同的新页面.
由于您是在数组边界之外编写的,因此代码的行为未定义.
任何事情都可能发生,这是未定义行为的本质,包括缺少段错误(编译器没有义务执行边界检查).
你写的是你没有分配的内存,但恰好在那里,而且 - 可能 - 没有被用于其他任何东西.如果对代码中看似无关的部分,操作系统,编译器,优化标记等进行更改,则代码的行为可能会有所不同.
换句话说,一旦你进入那个领域,所有的赌注都会被取消.