Why/when 以包含 C 字符串的终止字符“\0”?

Why/when to include terminating '\0' character for C Strings?

我是 C 的新手,对于何时需要手动将终止字符“\0”添加到字符串,我感到有些困惑。给定这个函数来计算字符串长度(为了清楚起见):

int stringLength(char string[])
{
    int i = 0;
    while (string[i] != '[=11=]') {
        i++;
}
    return i;
}

根据空终止字符计算字符串的长度。那么,使用以下情况,'\0'字符的作用是什么?

案例 1:

char * stack1 = "stack"; 
printf("WORD %s\n", stack1);
printf("Length %d\n", stringLength(stack1));

打印:

WORD stack
Length 5

案例 2:

char stack2[5] = "stack";
printf("WORD %s\n", stack2);
printf("Length %d\n", stringLength(stack2));

打印:

WORD stack���
Length 8

(这些结果每次都不同,但永远不会正确)。

案例 3:

char stack3[6] = "stack";
printf("WORD %s\n", stack3);
printf("Length %d\n", stringLength(stack3));

打印:

WORD stack
Length 5

案例 4:

char stack4[6] = "stack";
stack4[5] = '[=18=]';
printf("WORD %s\n", stack4);
printf("Length %d\n", stringLength(stack4));

打印:

WORD stack
Length 5

案例 5:

char * stack5 = malloc(sizeof(char) * 5);
if (stack5 != NULL) {
    stack5[0] = 's';
    stack5[1] = 't';
    stack5[2] = 'a';
    stack5[3] = 'c';
    stack5[4] = 'k';
    printf("WORD %s\n", stack5);
    printf("Length %d\n", stringLength(stack5));
}
free(stack5);

打印:

WORD stack
Length 5

案例 6:

char * stack6 = malloc(sizeof(char) * 6);
if (stack6 != NULL) {
    stack6[0] = 's';
    stack6[1] = 't';
    stack6[2] = 'a';
    stack6[3] = 'c';
    stack6[4] = 'k';
    stack6[5] = '[=22=]';
    printf("WORD %s\n", stack6);
    printf("Length %d\n", stringLength(stack6));
}
free(stack6);

打印:

WORD stack
Length 5

也就是说,我想知道情况 1、2、3 和 4 之间的区别(以及为什么情况 2 的行为不稳定,并且不需要在 1 和 3 中指定空终止字符。此外, 3 和 4 如何工作相同?)以及 5 和 6 如何打印出相同的东西,即使在情况 5 中没有为空终止字符分配足够的内存(因为只为 [= 中的每个字母分配了 5 个字符槽) 58=],它如何检测'\0'字符,即第6个字符?)

对于这个长得荒谬的问题,我感到非常抱歉,只是我在其他任何地方都找不到对这些特定实例的良好教学解释

字符串的存储必须始终为终止空字符留出空间。在您的某些示例中,您没有这样做,而是明确给出了 5 的长度。在这些情况下,您将得到未定义的行为。

String literals 始终自动获取空终止符。尽管strlen returns 长度为5,但实际上占用了6 个字节。

您的案例 5 之所以有效,是因为 undefined 有时意味着它看起来有效。您可能在内存中的字符串后面有一个零值 - 但您不能依赖它。

在情况 1 中,您正在创建一个字符串文字(将在只读内存中的常量),其中将隐式添加 [=13=]

由于 [=13=] 的位置依赖于找到字符串的结尾,因此您的 stringLength() 函数打印 5.

在情况 2 中,您尝试使用 5 个字符的字符串初始化大小为 5 的字符数组,不为 [=13=] 分隔符留下 space。与字符串相邻的内存可以是任何东西,并且可能在某处有一个 [=13=] 。这个 [=13=] 被认为是字符串的结尾,它解释了你得到的那些奇怪的字符。似乎对于您提供的输出,此 [=13=] 仅在另外 3 个字符之后才被发现,这些字符在计算字符串长度时也被考虑在内。由于内存的内容随时间变化,输出可能并不总是相同的。

在情况 3 中,您正在使用大小为 5 的字符串初始化大小为 6 的字符数组,留下足够的 space 来存储将被隐式存储的 [=13=]。因此,它将正常工作。

案例4与案例3类似,没有做任何修改

char stack4[5] = '[=10=]';

因为 stack4 的大小是 6,因此它的最后一个索引是 5。你正在用它的旧值本身覆盖一个变量。 stack4[5] 甚至在您覆盖它之前就已经包含了 [=13=]

在情况 5 中,您已经用字符完全填充了字符数组,没有为 [=13=] 留下 space。然而,当您打印字符串时,它打印正确。我认为这是因为与 malloc() 分配的内存相邻的内存恰好为零,即 [=13=] 的值。但这是未定义的行为,不应依赖。真正发生的事情取决于实施。
需要注意的是 malloc() 不会初始化它分配的内存,不像 calloc().

两者都

char str[2]='[=11=]';

char str[2]=0;

都一样。

但你不能指望它为零。由于操作系统的工作和安全原因,动态分配的内存可能具有零作为默认值。有关详细信息,请参阅 here and here

如果需要动态分配内存的默认值为零,可以使用calloc().

案例6最后是[=13=],其他位置是字符。打印时应显示正确的字符串。