c - 需要 fgets 中的最后一个 '\0'

标签 c string format fgets

我见过 fgets 的几种用法(例如,here),如下所示:

char buff[7]="";

(...)

fgets(buff, sizeof(buff), stdin);

有趣的是,如果我提供像“aaaaaaaaaaa”这样的长输入,fgets 会将其截断为“aaaaaa”,因为第 7 个字符将用于存储 '\0'.

但是,这样做时:

int i=0;
for (i=0;i<7;i++)
{
    buff[i]='a';
}
printf("%s\n",buff);

我总是会得到 7 个'a',而且程序不会崩溃。但如果我尝试写 8 个 'a',它就会。

正如我后来看到的那样,至少在我的系统上,当我分配 char buff[7] 时(有或没有 =""),第 8 个字节(从 1 开始计数,而不是从 0 开始)被设置为 0。据我猜测,事情就是这样完成的,这样一个 for 循环有 7 次写入,然后是一个string 格式化读取,可以成功,不管要写入的最后一个字符是不是'\0',从而避免程序员在写入 chars 时需要自己设置最后一个'\0'单独。

据此,在这种情况下

fgets(buff, sizeof(buff), stdin);

然后提供太长的输入,生成的 buff 字符串将自动包含两个 '\0' 字符,一个在数组内,一个紧跟在它后面由系统编写。

我也观察到这样做

fgets(buff,(sizeof(buff)+17),stdin);

仍然可以工作,并输出一个很长的字符串,而不会崩溃。据我猜测,这是因为fgets会一直写到sizeof(buff)+17,而最后要写入的字符正好是一个'\0',确保任何即将到来的字符串读取过程都将正确终止(尽管内存无论如何都搞砸了)。

但是,fgets(buff, (sizeof(buff)+1),stdin); 呢?这会用完所有在 buff 中正确分配的空间,然后在它后面写一个 '\0',从而覆盖...... '\0' 之前由系统写入。换句话说,是的,fgets 会越界,但可以证明,当写入长度只加一时,程序永远不会崩溃。

所以最后,问题来了:为什么 fgets 总是以 '\0' 终止其写入,而另一个 '\0' ,由系统放在数组后面,已经存在?为什么不像在一个接一个的 for 循环写入中那样,它可以访问整个数组并写入程序员想要的任何东西,而不会危及任何东西?

非常感谢您的回答!

编辑:事实上,没有可能的证据,只要我不知道这个在分配 buff[7] 时神秘出现的第 8 个 '\0' 是否是 C 的一部分标准与否,特别是对于字符串数组。如果没有,那么......它能起作用只是运气 :-)

最佳答案

but it can be proven that when adding only one to the length of the write, the program will never crash.

不!你无法证明这一点!不是在数学证明的意义上。您只表明在您的系统上,使用您的编译器,使用您使用的那些特定编译器设置,使用特定的环境配置,它可能不会崩溃。这远非数学证明!

事实上 C 标准本身,虽然它保证你可以获得“数组最后一个元素之后的一个位置”的地址,但它也声明取消引用该地址(即尝试从该地址读取或写入)是未定义的行为

这意味着在这种情况下,实现可以做一切。它甚至可以用天真的推理来做你期望的事情(即工作 - 但它纯粹是运气),但它也可能崩溃或者它也可能格式化你的高清(如果你非常非常不幸)。在编写系统软件(例如设备驱动程序或在裸机上运行的程序)时尤其如此,即当没有操作系统可以保护您免受编写错误代码的最恶劣后果时!

编辑 这应该回答评论中提出的问题(C99 标准草案):

7.19.7.2 The fgets function

Synopsis

#include <stdio.h>
char *fgets(char * restrict s, int n,
    FILE * restrict stream);

Description

The fgets function reads at most one less than the number of characters specified by n from the stream pointed to by stream into the array pointed to by s. No additional characters are read after a new-line character (which is retained) or after end-of-file. A null character is written immediately after the last character read into the array.

Returns

The fgets function returns s if successful. If end-of-file is encountered and no characters have been read into the array, the contents of the array remain unchanged and a null pointer is returned. If a read error occurs during the operation, the array contents are indeterminate and a null pointer is returned.

编辑:由于问题似乎出在对字符串是什么的误解,因此这是标准(强调我的)的相关摘录:

7.1.1 Definitions of terms

A string is a contiguous sequence of characters terminated by and including the first null character. The term multibyte string is sometimes used instead to emphasize special processing given to multibyte characters contained in the string or to avoid confusion with a wide string. A pointer to a string is a pointer to its initial (lowest addressed) character. The length of a string is the number of bytes preceding the null character and the value of a string is the sequence of the values of the contained characters, in order.

关于c - 需要 fgets 中的最后一个 '\0',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18496428/

相关文章:

string - 我怎样才能使用带有格式的动态格式字符串!宏观?

string - Stata:如何将字符串变量更改为日期?

r - 如何迭代保留结果格式的列表?

c++ - 指向多维静态分配数组部分的指针的语法

python - 在 Clutter 中使用 BindConstraint 来约束 actor 的大小

java - 在 Java 中使用 substring()

python - 如何在python中找到字符串向量之间的所有组合

python - 如何删除以某物开头和结尾的子字符串?

c - Z1 motes - 基站和传感节点之间的通信

c - 如何解决错误: else without a previous if