为什么当我溢出分配的字符数组时,我的C程序不会崩溃?

时间:2013-08-27 16:16:50

标签: c arrays memory stack

我有一个简单的C文件I / O程序,它演示了逐行读取文本文件,并将其内容输出到控制台:

/**
* simple C program demonstrating how
* to read an entire text file
*/

#include <stdio.h>
#include <stdlib.h>

#define FILENAME "ohai.txt"

int main(void)
{
    // open a file for reading
    FILE* fp = fopen(FILENAME, "r");

    // check for successful open
    if(fp == NULL)
    {
        printf("couldn't open %s\n", FILENAME);
        return 1;
    }

    // size of each line
    char output[256];

    // read from the file
    while(fgets(output, sizeof(output), fp) != NULL)
        printf("%s", output);

    // report the error if we didn't reach the end of file
    if(!feof(fp))
    {
        printf("Couldn't read entire file\n");
        fclose(fp);
        return 1;
    }

    // close the file
    fclose(fp);
    return 0;
   }

看起来我已经为每行256个字符分配了一个空格数组(32位机器上1024 bytes 位)。即使我在第一行填写ohai.txt超过1000个字符的文本,程序也不会出现段错误,我认为它会溢出,因为它溢出了{{{{{{[{[{ 1}}数组。

我的假设是操作系统会为程序提供额外的内存,同时它有额外的内存可用。这意味着当output[]中的一行文本占用的内存导致堆栈溢出时,程序才会崩溃。

具有更多C和内存管理经验的人是否支持或反驳我的假设,即为什么这个程序不会崩溃,即使文本文件的一行中的字符数远大于256?

4 个答案:

答案 0 :(得分:6)

这里没有溢出任何内容:fgets不会向缓冲区写入超过sizeof(output)个字符,因此不会溢出任何内容(请参阅the documentation)。

但是,如果溢出缓冲区,则会出现未定义的行为。根据C规范,程序可以执行任何事情:崩溃,不崩溃,无声地破坏重要数据,意外调用rm -rf /等等。所以,不要指望程序崩溃,如果你调用UB。

答案 1 :(得分:2)

OP的程序崩溃,因为没有缓冲区溢出

while(fgets(output, sizeof(output), fp) != NULL)
  printf("%s", output);

fgets()很好地阅读了一组char,最多为一个或255或\n。然后printf("%s" ...很好地将它们打印出来。重复这一过程,直到没有更多的数据/

没有崩溃,没有溢出,没有运行,没有命中,没有错误。

答案 2 :(得分:0)

fgets(output,sizeof(output),fp)读取(sizeof(输出)-1)这种情况下的字符数(否则读取到换行符或文件末尾)

答案 3 :(得分:0)

堆栈的解释以及为什么即使你真的发生溢出也不会发生段错误(并且正如其他人指出所写的代码不会)

你的堆栈指针从某个地址开始,比如说0x8000000然后运行时调用main并且它会向下移动一点(可能还有其他的东西,所以我们不知道堆栈的开头有多少东西main),然后main将为所有它的局部变量移动堆栈指针。所以在这一点上你的数组将有一个低于0x8000000超过256字节的地址,除非你在所有main的堆栈帧和任何其他C运行时东西的堆栈帧上运行,否则你不会得到段错误。

因此,为了简单起见,假设你的数组最终得到的是0x7fffd00的基地址,即低于0x8000000的768字节,这意味着至少你必须溢出那么多才能得到段错误(好吧你可能会得到)主要返回时或者当你调用feof时的段错误,因为你用随机字符填充你的堆栈帧,但是我们正在讨论fgets()中的段错误,但即使那些可写的东西被映射到堆栈上方的页面也不会被贬低(大多数操作系统不太可能避免这样做,所以如果你溢出得足够远,你就会得到一个段错误。)

如果堆栈以另一种方式运行(即:向上增长),则必须运行整个最大大小的堆栈,在用户空间中通常非常大(Linux上的默认值为32位x86为2MB)但是我我非常确定x86堆栈会向下增长,因此不太适合您的情况。