2

我有一个简单的 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字节位)。即使我ohai.txt在第一行填充了超过 1000 个字符的文本,程序也不会出现段错误,我认为它会发生段错误,因为它溢出了分配给它的output[]数组指定的可用空间量。

我的假设是操作系统会给程序额外的内存,而它有额外的内存可供使用。这意味着程序只会在一行文本消耗的内存ohai.txt导致堆栈溢出时崩溃。

即使文本文件的一行中的字符数远大于 256,也可以在 C 和内存管理方面有更多经验的人支持或反驳我关于为什么该程序不会崩溃的假设吗?

4

4 回答 4

6

您不会在这里溢出任何东西:fgets不会向缓冲区写入超过sizeof(output)字符,因此不会溢出任何东西(请参阅文档)。

但是,如果确实溢出缓冲区,则会出现未定义的行为。根据 C 规范,程序可能会做任何事情:崩溃、不崩溃、静默销毁重要数据、意外调用rm -rf /等。因此,如果调用 UB,请不要指望程序会崩溃。

于 2013-08-27T16:34:44.230 回答
2

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

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

很好地阅读了一fgets()char最多计数或 255 或\n. 然后printf("%s" ...很好地打印出来。这重复直到没有更多数据/

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

于 2013-08-27T16:44:13.773 回答
0

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

于 2013-08-27T16:31:07.623 回答
0

解释堆栈以及为什么即使您确实溢出也可能不会出现段错误(正如其他人指出的那样,编写的代码不会)

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

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

如果堆栈以其他方式运行(即:向上增长),您必须运行整个最大大小堆栈,这在用户空间中通常非常大(Linux 上 32 位 x86 的默认值为 2MB)但我很漂亮确保 x86 堆栈向下增长,因此您的情况不太可能。

于 2013-08-27T17:36:19.097 回答