当未连接的代码行被取消注释时,为什么这个 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)
在你的情况下应该是 6
和 sizeof(string)
这是数组的大小,包括 '[=13=]'
字节! ;-)
如果字符串声明为具有“自动大小”的数组,则差异只有一个,但如果您有 char string[256]
,sizeof(string)
将与 [=16= 不同].
对于 char *string
,sizeof(string)
可能是 8
或 4
。
@SteveSummit 已经详细解释了一切。这是一个简短的回答。
访问元素 lista[sizeof(lista)]
是未定义的行为,因此讨论它应该具有什么值是“毫无意义的”。我引用毫无意义,因为了解未定义的行为如何表现出来以进行调试可能是一件好事。但是如果这段代码要投入生产,你永远不应该访问 lista[sizeof(lista)]
。总是越界,总是bug。
我目前正在学习 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)
在你的情况下应该是 6
和 sizeof(string)
这是数组的大小,包括 '[=13=]'
字节! ;-)
如果字符串声明为具有“自动大小”的数组,则差异只有一个,但如果您有 char string[256]
,sizeof(string)
将与 [=16= 不同].
对于 char *string
,sizeof(string)
可能是 8
或 4
。
@SteveSummit 已经详细解释了一切。这是一个简短的回答。
访问元素 lista[sizeof(lista)]
是未定义的行为,因此讨论它应该具有什么值是“毫无意义的”。我引用毫无意义,因为了解未定义的行为如何表现出来以进行调试可能是一件好事。但是如果这段代码要投入生产,你永远不应该访问 lista[sizeof(lista)]
。总是越界,总是bug。