【问题标题】:Why/when to include terminating '\0' character for C Strings?为什么/何时为 C 字符串包含终止 '\0' 字符?
【发布时间】:2017-09-17 05:33:42
【问题描述】:

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

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

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

案例一:

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] = '\0';
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] = '\0';
    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 中没有为空终止字符分配足够的内存(因为“slack”中的每个字母只分配了 5 个字符槽,它如何检测'\0'字符,即第6个字符?)

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

【问题讨论】:

  • 很广泛地说,如果你有一个字符串存储在一个数组中,那么你必须有某种方法知道字符串的结束位置。两种最明显的方法是 (1) 保持单独的字符计数,或 (2) 用一些唯一字符终止字符串(例如 '\0')。选项 2 似乎是当今最常用的方法,C 会自动以'\0' 终止字符串常量。标准 C 库也排除'\0'-终止的字符串。
  • @TomKarzes:“C 自动用 '\0' 终止字符串常量”,只是字符串-literals
  • 不要尝试通过试错来学习 C,因为这会导致抑郁。

标签: c string c-strings null-terminated


【解决方案1】:

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

String literals 总是自动获得空终止符。尽管strlen 返回的长度为 5,但它确实占用了 6 个字节。

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

【讨论】:

    【解决方案2】:

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

    由于依赖\0 的位置来查找字符串的结尾,因此您的stringLength() 函数将打印5

    在第 2 种情况下,您尝试使用包含 5 个字符的字符串来初始化大小为 5 的字符数组,而不会为 \0 分隔符留出空间。与字符串相邻的内存可以是任何东西,并且可能在某处有\0。这个\0 在这里被认为是字符串的结尾,它解释了你得到的那些奇怪的字符。似乎对于您给出的输出,这个\0 仅在另外 3 个字符之后才被发现,在计算字符串长度时也考虑了这些字符。由于内存的内容会随着时间的推移而变化,因此输出可能并不总是相同的。

    在第 3 种情况下,您使用大小为 5 的字符串初始化大小为 6 的字符数组,留出足够的空间来存储将隐式存储的 \0。因此,它将正常工作。

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

    char stack4[5] = '\0';
    

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

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

    两者

    char str[2]='\0';
    

    char str[2]=0;
    

    都是一样的。

    但你不能依赖它为零。由于操作系统的工作和出于安全原因,动态分配的内存可能具有零作为默认值。请参阅herehere 了解更多信息。

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

    案例 6 末尾有 \0,其他位置有字符。打印时应显示正确的字符串。

    【讨论】:

    • “情况 5...我认为这是因为与 malloc() 分配的内存相邻的内存为零” - 可以/不可能是真的.这是因为你调用了Undefined Behavior——任何事情都可能发生,如果内存中的下一个字节恰好是0,那么它可以工作,但不能保证会发生什么——和 malloc 不初始化内存。但所有考虑,当然值得一票,如果你修正那个声明会更好。
    • @DavidC.Rankin 感谢您纠正我。我编辑了它。
    • 这对我的理解有很大帮助,非常感谢@J...S!如果我没有明确添加行 stack6[5] = '\0'; 会发生什么情况 6,因为它不是字符串文字,它会添加 '\0' 字符吗?我认为不会,因为我明确添加了字符。因此,如果我使用malloc 而不是calloc,这是否会导致终止字符的未定义行为(即依赖于以前的内存使用情况)?
    猜你喜欢
    • 2010-11-12
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    • 2018-09-26
    • 2014-02-01
    • 2014-12-03
    • 1970-01-01
    • 2020-12-19
    相关资源
    最近更新 更多