C 字符串中 '\0' 的效用

Utility of '\0' in C string

#include <stdio.h>
#include <string.h>
int main()
{
    char ch[20] = {'h','i'};
    int k=strlen(ch);
    printf("%d",k);
    
    return 0;
} 

输出为2。

据我所知,'\0' 有助于编译器识别字符串的结尾,但此处的输出表明 strlen 可以自行检测结尾那么为什么我们需要 '\0'?

空字节(即值 0)定义了 C 中字符串的结尾。

当您定义 ch 时,您提供的初始值设定项少于数组中的值,因此剩余元素设置为 0。这导致以空字符结尾的字符串。

strlen 函数基本上是在查找该值并计算在找到空字节之前它看到了多少个元素。

在此声明中:

char ch[20] = {'h','i'};

前两个元素被显式初始化,所有其他元素由零隐式初始化。

上面的声明实际上(除了数组的第三个元素也被显式初始化之外)等同于:

char ch[20] = "hi";

注意字符串文字表示为如下数组:

{ 'h', 'i', '[=12=]' }

即数组中包含一个以零字符结尾的字符串'[=14=]',函数strlen可以成功找到存储字符串的长度

如果你会这样写:

char ch[2] = "hi";

那么在这种情况下,数组 ch 没有 space 来存储字符串文字的终止零。在这种情况下,将函数 strlen 应用于此数组会调用未定义的行为。

您的数组 ch 可能包含零,因此 i 之后的字节已设置为零。您可以使用调试器查看它或简单地在代码中对其进行测试。相信我,strlen 需要零才能工作。

长话短说:是您的编译器根据标准做出主动决策。

长话短说:

char ch[20] = {'h','i'}

在上面的行中,您向编译器暗示的是;

  • 分配足够大的内存来存储 20 个字符(又名,20 个字符的数组)。
  • 将前两个切片(数组的前两个成员)初始化为'h' & 'i'。
  • 隐式初始化其余部分。

因为你正在初始化你的 char 数组,你的编译器足够聪明,可以在第三个元素有足够的 space 剩余时将空终止符插入到第三个元素中。这个过程是初始化的标准。

如果您要删除初始化语法并像下面那样手动初始化每个成员,结果是未定义的行为。

char ch[20];
ch[0] = 'h';
ch[1] = 'i';

另外,如果你的编译器没有额外的 space 来放置空终止符,即使你使用了初始化器,结果仍然是未定义的行为,因为你可以通过这段代码轻松测试以下片段:

char ch[2] = { 'h','i' };

int k = strlen(ch);
printf("%d\n%s\n", k, ch);

现在,如果您要将 'ch' 的数组大小从 2 增加到 3 或任何其他大于 2 的数字,您会看到编译器使用空终止符对其进行初始化,因此不再有未定义的行为.

As far as I know '[=14=]' helps compiler identify the end of string

从技术上讲,它可以帮助用户代码和 C 运行时库识别字符串的结尾。在某种程度上,编译器需要知道字符串在哪里结束,它不需要寻找终止符就知道了。

but the output here suggests the strlen can detect the end on it's own

那是一种误解。实际情况是您的字符串是 null-terminated 即使您没有明确地在其中放置空终止符。这是使用仅指定部分元素值的初始化程序声明数组的结果。正如您的其他一些答案更详细地描述的那样,这不会产生部分初始化。相反,初始化程序未为其指定值的元素是 default-initialized。对于 char 类型的元素,这意味着初始化为 0,用作字符串终止符。

此外,如果数组 而没有终止符,那么将其传递给 strlen() 的结果将是未定义的。您无法从结果中得出任何结论。

then why do we need '[=16=]'?

这样用户代码和许多标准库函数就可以识别字符串的结尾。你已经知道了。

但在很多情况下我们不需要显式提供终止符。特别是,我们不需要用字符串字面量来表示它们(这意味着与您可能想要的不同),并且您不需要在 char 数组存储字符串的初始值设定项中表示它们, 前提是数组的元素多于您在初始化程序中指定的元素。