我希望这会崩溃,但事实并非如此

我是一个80年代的老黑客,我手上有一些空闲时间以为我会’C’大惊小怪

(我的背景是Forth,它做的事情回到了前面所以我学到很多关于强迫很多错误的事情)

在这种情况下,我设置了一个小数组,我认为这将是3个元素的大小(0,1,2)

如果我运行编译,我本以为我会遇到一个越界错误,但是没有 – 它编译好了并且运行得很甜蜜

这一点并不是很匆忙,但对于那个小秃头来说并不好,所有这些都在刮擦。


int main() { char members[3][16]; // 3 elements, each 15 char long plus null printf("\n enter something.. "); scanf( "%s", members[4]); printf("\n and something else.. "); scanf( "%s", members[5]); printf(" %s ", members[4]); printf(" %s\n", members[5]); return 0; } 

C不进行任何边界检查。 (Forth也没有,所以我不确定期望来自哪里。)

溢出数组是未定义的行为 :允许(但不是必需)崩溃。 在这种情况下,字节恰好与放置局部变量的堆栈帧位于同一虚拟内存页中。 如果框架朝向页面的末尾,CPU将识别错误的地址并抱怨溢出。

如果你去几千字节或几兆字节,你可能会看到你期望的东西。

访问数组越界会调用未定义的行为 。 这意味着任何事情都可能发生,包括没有任何明显错误的运行。

通常,内存以4 kB页面分配。 因此,在最后一个变量之后会有一些额外的空间,并且您的缓冲区溢出未被检测到。 但是,如果在您的members数组之后有另一个变量并且您写入members[4] ,则另一个变量将被破坏。

像valgrind和dmalloc这样的工具通常用于检测缓冲区溢出。 他们的工作是在你的变量周围分配特殊的保护区域,并检查没有人在那里写过。

你错过了堆栈上的主要返回地址,可能会覆盖主要调用者的一些非关键数据,因此你的程序不会崩溃。 此外,在某些平台上(例如powerpc),堆栈不用于函数调用,返回地址存储在特殊寄存器中(在需要时将其推送到堆栈)。 所以这个错误的程序不会崩溃是正常的。

更新:此外,在一些系统堆栈上增长(到更高的地址)。 至少在ARM堆栈中,可以选择增长。