我有一个简单的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?
答案 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堆栈会向下增长,因此不太适合您的情况。