下面是C代码,它将生成char数组,但是显式添加了Null字符。在两个编译器中,结果都是出乎意料的,而且我不确定为什么我们还要显式添加Null字符?
//
// stringBugorNot.c
//
//
//
#include <string.h>
#include <stdio.h>
int main(void)
{
char aString[3] = {'a', 'b','c'};
char bString[4] = {'a', 'b', 'c', '\0'};
printf("\n");
printf("len of a is: %lu\n", strlen(aString));
printf("len of b is: %lu\n", strlen(bString));
printf("\n");
//Portion A
printf("last element of a is: '%c'\n", aString[strlen(aString)]);
printf("last element of b is: '%c'\n", bString[strlen(bString)]);
printf("\n");
//Portion B
printf("last element of a is: '%c'\n", aString[strlen(aString) - 1]);
printf("last element of b is: '%c'\n", bString[strlen(bString) - 1]);
}
评论
+ clang将给出运行时错误,因为“ aString”上的边界超出范围.. + gcc不会给出任何错误,只需按预期输出空值“ nothing”即可。但是也许gcc更聪明,为我添加了null?实际的内存大小是否不同?
Clang OUTPUT ---->
a的镜头是:3
b的len是:3
bugOrNot.c:16:41:运行时错误:'char [3]'类型的索引3超出范围
a的最后一个元素是:”
b的最后一个元素是:”
a的最后一个元素是:“ c”
b的最后一个元素是:“ c”
GCC输出---->
a的len是:9
b的len是:3
a的最后一个元素是:”
b的最后一个元素是:”
a的最后一个元素是:”
b的最后一个元素是:“ c”
答案 0 :(得分:5)
您看到的意外行为在C标准中称为未定义行为(UB):
strlen
上呼叫aString
是UB,因为没有空终止符aString
的未定义索引处取消引用是UB,除非索引为0、1或2 bString
来无意中插入空终止符。不过,这仍然不会改变它仍然是UB的事实。答案 1 :(得分:3)
在C中,字符串是字符值的序列,包括nul终止符。该终止符是各种C库例程如何知道字符串结尾的位置。如果您未正确终止字符串,则strlen
的{{1}}和strcpy
和printf
这样的库例程都将扫描字符串的末尾进入其他内存,从而导致输出乱码或运行时错误。
在两个不同的编译器中,%s
的长度得到不同结果的原因是,在clang情况下,紧跟a
的最后一个元素的字节为0,而在gcc中a
之后的字节不包含0的情况。
严格来说,将未终止的字符序列传递给字符串处理例程的行为是未定义-语言规范对编译器或运行时环境没有要求“做正确的事” ”,无论如何。到那时,您基本上已经取消了保修,几乎所有事情都可能发生。
请注意,C语言规范不要求对数组访问进行边界检查-事实上,您对clang的索引超出范围是由于编译器格外友好并且超出了语言标准的实际要求。
答案 2 :(得分:3)
当你说
char bString[4] = {'a', 'b', 'c', '\0'};
您已经正确构造了一个以空值结尾的字符串。就像你说的那样
char bString[4] = "abc";
由于这是一个正确的,以null终止的字符串,因此调用strlen(bString)
是有意义且合法的,您将得到3的结果。
当你说
char aString[3] = {'a', 'b','c'};
另一方面,正如我想您所知,您不是构造了一个正确的以null结尾的字符串。因此,调用strlen(aString)
是不合法或没有意义的-正式地说,我们说结果是 undefined ,这意味着绝对可能发生任何事情。
您使用两种不同的编译器尝试了该代码,但惊讶地得到了两种不同的结果。这是完全正常的。 (获得两个不同的结果是完全正常的,对此感到惊讶是完全正常的,因为在您初次遇到它的时候,是令人惊讶。)
一个编译器比另一个编译器“更聪明”,或者“猜测”您试图构造一个字符串并因此自动提供了“ missing” {{ 1}}。这只是偶然,偶然的偶然。 (当然也不是一个编译器或另一个编译器有任何类型的错误。同样,这里没有正确的结果,因此,无论做什么,编译器都不会出错。)
如果要在C语言中使用字符串,请确保它们都正确地以null终止。如果您偶然偶然对非正确终止的字符串进行类似字符串的操作,请不要尝试解释结果,不要以为它们有任何意义,尤其是不要确定它是“正确的”因此您可以信赖的结果。你不能它可能无缘无故地发生变化,例如下周您使用其他编译器,或者客户在重要数据而非测试数据上使用程序时。