当未连接的代码行被取消注释时,为什么这个 C 程序检测到字符串中的两个“\0”字符?

Why is this C program detecting two '\0' characters in a string when an unconnected line of code gets uncommented?

我目前正在学习 C,我编写了这个程序来检查 '\0' 字符是否真的在字符串的末尾,如“K 和 R”中所指出的那样。

虽然我得到了最奇怪的结果。

如果我评论“int lista[] = {0, 1, 2, 3, 4};”程序外的语句(这是一个与该程序的其他语句无关的语句,它是我要进行的另一个测试的一部分)。 程序的输出符合预期,检测到字符串结尾有一个 '\0' 字符。 但是,如果我不对语句进行注释,程序输出会在字符串末尾检测到 两个 '\0' 字符。 为什么会这样?

这是未注释语句的程序:

#include <stdio.h>

int main(void)
{
    int lista[] = {0, 1, 2, 3, 4};
    char string[] = "linhas";
    
    for (int i = 0; i <= sizeof(string); i++)
    {
        if (string[i] != '[=10=]')
        {
            printf("%c\n", string[i]);
        }
        else
        {
            printf("this dawmn null char\n");
        }
    }
}

这输出:

l
i
n
h
a
s
this dawmn null char
this dawmn null char

这是注释掉以下行的程序:

#include <stdio.h>

int main(void)
{
    /*int lista[] = {0, 1, 2, 3, 4};*/
    char string[] = "linhas";

    for (int i = 0; i <= sizeof(string); i++)
    {
        if (string[i] != '[=12=]')
        {
            printf("%c\n", string[i]);
        }
        else
        {
            printf("this dawmn null char\n");
        }
    }
}

它输出:

l
i
n
h
a
s
this dawmn null char

你的循环

for (int i = 0; i <= sizeof(string); i++)

总是有点错误。应该是

for (int i = 0; i < sizeof(string); i++)

通过使用 <=,您在循环中进行了太多次访问,并且访问了 string 数组之外的内存。看起来,在 lista 数组就位的情况下,您错误访问的额外字节(在 string 数组之外)恰好是 0,因此您会得到额外的第二个打印输出“this dawmn 空字符”消息。

但是,当您注释掉 lista 数组时,您错误访问的额外字节一定不是 0,因此它被打印为本身。它可能是一个不可见的控制字符,这就是为什么您什么都看不到的原因。我建议将您的代码更改为

if (string[i] != '[=12=]')
     printf("string contains %d\n", string[i]);
else printf("this damn null char\n");

为了看得更清楚。

这里的重要教训是,如果您有一个循环应该 运行 N 次,则有两种编写方法。在 C 中,绝大多数时候,你想把它写成

for(i = 0; i < N; i++)

这是一个“基于 0 的”循环,运行 从 0 到 N-1,总共 N 次行程。偶尔,你想要一个基于 1 的循环:

for(i = 1; i <= N; i++)

这 运行 从 1 到 N,总共 N 次。但是如果你写

for(i = 0; i <= N; i++)      /* usually WRONG */

你的循环 运行 从 0 到 N,总共 N+1 次。

不要混淆 strlen(string) 在你的情况下应该是 6sizeof(string) 这是数组的大小,包括 '[=13=]' 字节! ;-)

如果字符串声明为具有“自动大小”的数组,则差异只有一个,但如果您有 char string[256]sizeof(string) 将与 [=16= 不同].

对于 char *stringsizeof(string) 可能是 84

@SteveSummit 已经详细解释了一切。这是一个简短的回答。

访问元素 lista[sizeof(lista)] 是未定义的行为,因此讨论它应该具有什么值是“毫无意义的”。我引用毫无意义,因为了解未定义的行为如何表现出来以进行调试可能是一件好事。但是如果这段代码要投入生产,你永远不应该访问 lista[sizeof(lista)]。总是越界,总是bug。