一个字符串末尾的空字符'\0'

4
我有下面的代码。
#include <stdio.h>
#include <string.h>

#define MAXLINE 1000

int main()
{
    int i;
    char s[MAXLINE];

    for (i = 0; i < 20; ++i) {
        s[i] = i + 'A';
        printf("%c", s[i]);
    }

    printf("\nstrlen = %d\n", strlen(s)); // strlen(s): 20
    return 0;
}

我应该写什么?

s[i] = '\0';

循环执行结束后,字符串是通过显式标记结尾s[i] = '\0';还是自动完成的呢? 如果没有使用s[i] = '\0';函数,则strlen(s)将返回正确值20。


9
strlen函数需要一个以空字符'\0'结尾的字符串作为参数。如果你没有提供一个正确的以空字符结尾的字符串,那么会导致未定义行为。如果你上面的代码“能够运行”,那只是偶然而非有意为之。不要碰运气地编程。 - WhozCraig
2
在你的例子中,数组之前恰好包含了零,但不一定如此。始终终止字符串。 - Weather Vane
1
在使用 strlen 之前并且循环结束时,要执行 s[i+1]='\0' 或者甚至在循环之前就使用 memset(s,'\0',sizeof(s))。否则 strlen 等函数将无法正常工作。 - Gaurav
1
循环之后的 @Observer s[i] = '\0' 是最简单的,并且只需要写入一个字节。 - Weather Vane
4
除了 strlen 返回 size_t 之外,你必须使用 %zu 打印结果。 - phuclv
显示剩余3条评论
3个回答

5
是的,你需要自己添加空终止符。程序不会自动添加。
你可以通过将赋值为不包含20字节NUL的内容来验证这一点。
char s[MAXLINE] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

如果你这样做,strlen(s)不会返回20。

5

是的,在循环后应该添加空终止符。或者,您可以使用0初始化整个数组。这样,您就不必在循环后添加0,因为已经有一个了:

...
char s[MAXLINE] = {0};
...

4
这里提供的特定语法变体是一种默认初始化任何类型的自动变量的标准习语。需要注意的是,它只适用于初始化,而不是赋值语句。 - John Bollinger
这是次优的做法,因为这会导致更多的代码。这种方法会在比所需更多的内存上写入零,写入1000个零而不仅仅是一个(并且一些零将被覆盖)。 - Myst

1

你必须添加一个NUL终止符来标记C字符串的结尾。

除非文档声明某个函数调用会自动写入NUL终止符,否则添加NUL终止字符不是自动完成的。

在您的情况下,请使用:

s[20] = 0;

正如评论中所提到的,C字符串是由终止符NUL字符定义的。所有的strXXX C函数也需要使用NUL字符。
如果您没有使用NUL标记字符串的结尾,那么您就有了一个(二进制)字符序列,但不是C字符串。它们有时被称为二进制字符串,不能使用strXXX库函数。
为什么会得到正确的结果
你得到正确的结果很可能只是偶然的。最有可能的解释是,你正在使用的操作系统为你提供了一个"干净"的内存栈(初始栈内存都是零)......这并不总是正确的。
由于在执行代码之前从未写入堆栈内存,所以下一个字节是之前存在的任何东西(在您的操作系统上,当堆栈首次初始化时,该字节设置为零)。
然而,如果操作系统没有为您提供一个"干净"的堆栈,或者您的代码在先前使用过的堆栈上运行,这将不成立。

“你必须在字符串末尾添加NUL终止符”这个措辞听起来不太对。根据定义,终止的空字符已经包含在字符串中了。基本上,一个字符串是由以(并包括)空字符结尾的字符序列组成的。因此,“向字符串添加空字符”意味着在字符串末尾添加一个额外的空字符,这是不必要的。 - Lxer Lx
@LxerLx - 我理解你的观点。我已经更新了回答。有更好的吗? - Myst
2
现在看起来更好了。我认为,在这个问题的背景下,强调一点很重要,那就是如果一个字符序列没有以空字符结尾,则它不是一个字符串。 - Lxer Lx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接