C标准保证缓冲区是否触及它们的空终止符?

在为标准库的许多string函数提供缓冲区的各种情况下,是否保证缓冲区不会被修改超出空终止符? 例如:

char buffer[17] = "abcdefghijklmnop"; sscanf("123", "%16s", buffer); 

buffer现在是否需要等于"123\0efghijklmnop"

另一个例子:

 char buffer[10]; fgets(buffer, 10, fp); 

如果读取的行只有3个字符,可以确定第6个字符与fgets被调用之前是一样的吗?

缓冲区中的每个单独的字节都是一个对象。 除非sscanffgets的函数描述的某些部分提到修改这些字节,或者甚至暗示它们的值可能改变,例如通过声明它们的值变得没有指定,那么一般规则适用于:(强调我的)

6.2.4对象的存储时间

2 […]存在一个对象,具有一个常量地址, 并在其整个生命周期中保留其最后存储的值 。 […]

这是相同的原则,保证

 #include <stdio.h> int a = 1; int main() { printf ("%d\n", a); printf ("%d\n", a); } 

试图打印两次。 即使a是全局的, printf也可以访问全局variables, printf的描述没有提到修改a

fgets的描述和sscanf的描述都没有提到修改缓冲区超过实际上应该写入的字节(除非发生读错误),所以这些字节不会被修改。

C99标准草案没有明确说明在这些情况下应该发生什么,但是考虑到多种变化,你可以certificate它必须以某种方式工作,以便在所有情况下符合规范。

标准说:

%s – 匹配一系列非空白字符.252)

如果不存在l长度修饰符,则相应的参数应该是一个指向字符数组的初始元素的指针,该字符数组的大小足以接受该序列和一个终止的空字符,该字符将被自动添加。

这里有两个例子表明它必须按照你提出的符合标准的方式工作。

例子A:

 char buffer[4] = "abcd"; char buffer2[10]; // Note the this could be placed at what would be buffer+4 sscanf("123 4", "%s %s", buffer, buffer2); // Result is buffer = "123\0" // buffer2 = "4\0" 

例子B:

 char buffer[17] = "abcdefghijklmnop"; char* buffer2 = &buffer[4]; sscanf("123 4", "%s %s", buffer, buffer2); // Result is buffer = "123\04\0" 

请注意,sscanf的接口不能提供足够的信息来真正知道这些是不同的。 因此,如果示例B要正常工作,就不能混淆示例A中的空字符之后的字节。这是因为它必须根据规范的这一点在两种情况下工作。

所以它隐含地必须按照你所说的那样工作。

其他function也可以用类似的参数,但是我想你可以从这个例子中看到这个想法。

注:提供格式的大小限制(如“%16s”) 可能会改变行为。 根据规范,在将数据写入缓冲区之前,sscanf将缓冲区清零至其极限在function上是可接受的。 实际上,大多数实现select性能,这意味着他们只剩下剩下的部分。

当规范的意图是做这种调零的时候,通常是明确指定的。 strncpy就是一个例子。 如果string的长度小于指定的最大缓冲区长度,则会用空字符填充空格的其余部分。 事实上,这个相同的“string”函数可能会返回一个非终止的string,这使得人们最常用的function之一,滚动自己的版本。

就fgets而言,可能会出现类似的情况。 唯一的问题是规范明确规定,如果没有读入,缓冲区保持不变。 一个可接受的function实现可以通过在清零缓冲区之前检查是否至less有一个字节要读取来回避这个问题。

这个标准有点含糊不清,但我认为合理的解释是答案是:是的,不允许向缓冲​​区写入比读取+ null更多的字节。 另一方面,对文本的更严格的阅读/解释可能会得出结论:答案是否定的,不能保证。 以下是关于fgets的公开可用的草案 。

char *fgets(char * restrict s, int n, FILE * restrict stream) ;

fgets函数最多只读取一个比从stream指向的stream中指定的字符数less一个字符到s指向的数组。 换行符(保留)或文件结束后不会读取其他字符。 在读入数组的最后一个字符之后立即写入一个空字符。

如果成功, fgets函数将返回s 。 如果遇到文件结束并且没有字符被读入数组,则数组的内容保持不变并且返回空指针。 如果在操作期间发生读取错误,则数组内容不确定,并返回空指针。

有一个关于它应该从input读取多less的保证,即停止在换行符或EOF读取,并且不能读取多于n-1个字节。 虽然没有什么可以明确地说明它允许写入缓冲区的数量,但常识是fgetsn参数被用来防止缓冲区溢出。 有一点奇怪的是,这个标准使用了模糊的术语read ,如果你想对它使用的术语进行挑选,这可能不一定意味着gets不能写入超过n个字节的缓冲区。 但请注意,关于这两个问题使用了相同的“读取”术语: n -limit和EOF / newline限制。 所以,如果你将n相关的“读”解释为缓冲区写限制,那么[为了一致性],你可以/应该以相同的方式解释另一个“读”,即不要超过string短于缓冲区。

另一方面,如果区分短语动词“读入”(=“写入”)和“读取”的用法,则不能以相同的方式读取委员会的文本。 你可以保证它不会“读入”(=“写入”)超过n个字节的数组,但是如果inputstring被换行符或EOF更早地终止,那么只能保证input的其余部分将不会被“读”,但是否意味着将不被“读入”(=“写入”)在这个更严格的阅读下缓冲区是不清楚的。 关键的问题是关键字“进”,这是被忽略的,所以问题是我在括号中给出的修改后的引用是否是预期的解释:

在换行符(保留)或文件结束后,没有其他字符被读入数组中。

坦白地说,一个单一的后置条件 (在这种情况下会很短)将比我所引用的措辞更有帮助…

我不想去尝试和分析他们关于*scanf家族的文章,因为我怀疑在这些function中发生的所有其他事情会变得更加复杂; 他们写的fscanf大约五页长…但我怀疑类似的逻辑适用。

是否保证缓冲区不会被修改超出空终止符?

不,没有保证。

缓冲区现在是否需要等于“123 \ 0efghijklmnop”?

是。 但那只是因为你已经使用了正确的参数来处理string相关的函数。 如果你搞砸缓冲区的长度,input修改器sscanf等,那么你的程序将编译。 但是它在运行时很可能会失败。

如果读取的行只有3个字符,可以确定第6个字符与fgets被调用之前是一样的吗?

是。 一旦fgets()数字你有一个3个字符的inputstring,它将input存储在提供的缓冲区,它根本不关心提供的空间的重置。

缓冲区现在是否需要等于“123 \ 0efghijklmnop”?

这里的buffer是由123保证终止在NULstring。

是的,为数组buffer分配的内存不会被取消分配,但是您要确保/限制您的stringbuffer最多只能有16字符元素,您可以在任何时间读入它。 现在取决于你是只写一个char还是最大可以使用什么buffer

例如:

 char buffer[4096] = "abc";` 

实际上做了一些事情,

 memcpy(buffer, "abc", sizeof("abc")); memset(&buffer[sizeof("abc")], 0, sizeof(buffer)-sizeof("abc")); 

标准强调,如果字符数组的任何部分都被初始化了,那么它就是在任何时刻都一直服从它的内存边界。

标准没有任何保证,这就是为什么函数sscanffgets被推荐使用(关于缓冲区的大小),就像你在问题中显示的那样(并且使用fgets被认为比fgets更好)。

然而,一些标准函数在他们的工作中使用了null-terminator,例如strlen (但是我想你问一下string修改)

编辑:

在你的例子中

 fgets(buffer, 10, fp); 

保证10个字符以后的字符不变( fgets不会考虑buffer内容和长度)

EDIT2:

而且,当使用fgets请记住'\n'将被存储在缓冲区中。 例如

  "123\n\0fghijklmnop" 

而不是预期的

  "123\0efghijklmnop" 

取决于使用中的function(在较低的程度上它的实现)。 当sscanf遇到第一个非空白字符时,它将开始写入,并继续写入,直到它的第一个空白字符,它将添加一个结束符0并返回。 但是像strncpy (着名)这样的函数strncpy缓冲区的其余部分strncpy

然而,在C标准中没有规定这些function如何performance的内容。