C 中的 gets() 和缓冲区
gets() and buffer in C
我知道,使用 gets() 是一个非常糟糕的主意,因为它可能会导致缓冲区溢出。但我有一些疑问。
假设一个c程序有如下代码-
char word[6];
gets(word);
puts(word);
如果我输入例如 -
HELLO WORLD
,假设gets()
将其读取为[H] [E] [L] [L] [O] [ ]
,其余进入输入缓冲区是否正确?
如果发生这种情况,puts()
如何获取数据以显示完整的字符串?
不,假设它用完就停止是不正确的space;它不知道有多少 space 可用。 gets
只是不断地读入字符并写入邻近的内存,调用未定义的行为。您可能会很幸运,并且没有使用相邻的内存(在 gets
之前或之后),并且 puts
"just works"。或者它可能会覆盖您的堆栈指针,然后一切都会爆炸。或者它可能正确写入缓冲区,但缓冲区的位在 puts
到达之前被覆盖。或其他任何东西;这是未定义的行为。
不要这样做。永远不要使用 gets
.
gets
函数一次读取整行。因此,在您的示例中,它将尝试将 11 个字符的 "HELLO WORLD" 读取到只有 6 个字符宽的缓冲区中。这会导致缓冲区溢出 undefined behavior.
并且因为 gets
无法限制它可以读取的字符数,这使得它很危险,这就是它从 C11 标准中删除的原因。
is it correct to assume that gets() reads it as [H] [E] [L] [L] [O] [ ], and the rest goes into the input buffer ?
没有。试图溢出缓冲区 word[]
是 未定义的行为。任何事情都可能发生。其余代码无关紧要。发生这种情况时,没有指定缓冲区的内容。
你的问题表明你认为 gets
可能以某种方式知道 word
只有 6 个字符长,所以它只用 6 个字符填充它并将其余的留在与输入流关联的缓冲区中.事实并非如此。调用 gets(word)
仅将 word
的起始地址传递给 gets
。这就是它收到的全部信息——一个起始位置。它不接收有关长度的任何信息。 gets
从输入流中读取,直到读取换行符或遇到文件结尾或发生错误。
如果你输入“HELLO WORLD”,程序打印出来,是因为gets
读取数据写入内存,超出了word
的范围。没有任何奇特的缓冲或交互发生——只是被写入未分配给该目的的内存。它可能会破坏您程序中的某些内容。但看起来你很“幸运”,因为错误并没有立即破坏你的程序,数据一直在那里,直到 puts
可以从内存中读取它并将其写入输出。
但是,您永远不应该期待这种行为。以这种方式工作的一个原因是你有一个非常简单的程序,它没有对内存做任何其他事情。在更复杂的程序中,有很多对象和活动,溢出缓冲区更有可能以多种方式破坏程序。
这正是 gets
的问题;一旦读取到 6 个字符,它 将不会 停止,它将继续读取,直到它看到一个换行符,并将这些字符立即分配给 word
缓冲区末尾后的内存。这就是缓冲区溢出 是 。如果 word
缓冲区之后的几个字节没有任何 重要的 内容(例如堆栈帧的 return 地址,或另一个局部变量),则覆盖内存不会导致 明显的 问题,并且 puts
会做几乎相同的事情 - 从 word
和它后面的内存中读取 直到它看到字符串终止符。
我知道,使用 gets() 是一个非常糟糕的主意,因为它可能会导致缓冲区溢出。但我有一些疑问。
假设一个c程序有如下代码-
char word[6];
gets(word);
puts(word);
如果我输入例如 -
HELLO WORLD
,假设gets()
将其读取为[H] [E] [L] [L] [O] [ ]
,其余进入输入缓冲区是否正确?
如果发生这种情况,puts()
如何获取数据以显示完整的字符串?
不,假设它用完就停止是不正确的space;它不知道有多少 space 可用。 gets
只是不断地读入字符并写入邻近的内存,调用未定义的行为。您可能会很幸运,并且没有使用相邻的内存(在 gets
之前或之后),并且 puts
"just works"。或者它可能会覆盖您的堆栈指针,然后一切都会爆炸。或者它可能正确写入缓冲区,但缓冲区的位在 puts
到达之前被覆盖。或其他任何东西;这是未定义的行为。
不要这样做。永远不要使用 gets
.
gets
函数一次读取整行。因此,在您的示例中,它将尝试将 11 个字符的 "HELLO WORLD" 读取到只有 6 个字符宽的缓冲区中。这会导致缓冲区溢出 undefined behavior.
并且因为 gets
无法限制它可以读取的字符数,这使得它很危险,这就是它从 C11 标准中删除的原因。
is it correct to assume that gets() reads it as [H] [E] [L] [L] [O] [ ], and the rest goes into the input buffer ?
没有。试图溢出缓冲区 word[]
是 未定义的行为。任何事情都可能发生。其余代码无关紧要。发生这种情况时,没有指定缓冲区的内容。
你的问题表明你认为 gets
可能以某种方式知道 word
只有 6 个字符长,所以它只用 6 个字符填充它并将其余的留在与输入流关联的缓冲区中.事实并非如此。调用 gets(word)
仅将 word
的起始地址传递给 gets
。这就是它收到的全部信息——一个起始位置。它不接收有关长度的任何信息。 gets
从输入流中读取,直到读取换行符或遇到文件结尾或发生错误。
如果你输入“HELLO WORLD”,程序打印出来,是因为gets
读取数据写入内存,超出了word
的范围。没有任何奇特的缓冲或交互发生——只是被写入未分配给该目的的内存。它可能会破坏您程序中的某些内容。但看起来你很“幸运”,因为错误并没有立即破坏你的程序,数据一直在那里,直到 puts
可以从内存中读取它并将其写入输出。
但是,您永远不应该期待这种行为。以这种方式工作的一个原因是你有一个非常简单的程序,它没有对内存做任何其他事情。在更复杂的程序中,有很多对象和活动,溢出缓冲区更有可能以多种方式破坏程序。
这正是 gets
的问题;一旦读取到 6 个字符,它 将不会 停止,它将继续读取,直到它看到一个换行符,并将这些字符立即分配给 word
缓冲区末尾后的内存。这就是缓冲区溢出 是 。如果 word
缓冲区之后的几个字节没有任何 重要的 内容(例如堆栈帧的 return 地址,或另一个局部变量),则覆盖内存不会导致 明显的 问题,并且 puts
会做几乎相同的事情 - 从 word
和它后面的内存中读取 直到它看到字符串终止符。