为什么C字符串(字符数组)被合并在一起,或者有时在C中打印错误



我一直在C中处理字符串。在处理声明和初始化字符串的方法时,我发现了一些我不理解的奇怪行为。

#include<stdio.h>
#include<string.h>
int main()
{
char str[5] = "World";
char str1[] = "hello";
char str2[] = {'N','a','m','a','s','t','e'};
char* str3 = "Hi";
printf("%s %zun"
"%s %zun"
"%s %zun"
"%s %zun",
str, strlen(str),
str1, strlen(str1),
str2, strlen(str2),
str3, strlen(str3));
return 0;
}

样本输出:

Worldhello 10
hello 5
Namaste 7
Hi 2

在某些情况下,上面的代码使str包含Worldhello,其余代码与初始化时一样。在其他一些情况下,上面的代码使str2包含Namastehello。它发生在我从未连接过的不同变量中。那么,它们是如何结合在一起的呢?

要处理字符串,必须在每个字符串的末尾留出一个空字符的空间。在有char str[5]="World";的情况下,只允许使用五个字符,编译器会用"World"填充它们,但它们后面没有空字符的空间。尽管字符串文字"World"在其末尾包含一个自动空字符,但您没有在数组中为其提供空间,因此不会复制它。

在有char str1[]="hello";的情况下,编译器通过计算字符(包括字符串文字末尾的null字符)来确定数组大小。

在有字符str2[]={'N','a','m','a','s','t','e'};的情况下,没有字符串文字,只有单个字符的列表。编译器通过对数组大小进行计数来确定数组大小。由于没有空字符,因此不为其提供空间。

未能用null字符终止字符串的一个潜在后果是,printf将继续读取字符串之外的内存,并根据它找到的值打印字符。当编译器将其他字符数组放在要打印的数组之后时,这些数组中的字符可能会出现在输出中。

如果在str中为空字符留出空间,并在str2中提供零值,则程序将按顺序打印字符串:

#include <stdio.h>
#include <string.h>
int main(void)
{
char str[6] = "World"; // 5 letters plus a null character.
char str1[] = "hello";
char str2[] = {'N', 'a', 'm', 'a', 's', 't', 'e',  0}; // Include a null.
char *str3 = "Hi";
printf("%s %zun%s %zun%s %zun%s %zun",
str,  strlen(str),
str1, strlen(str1),
str2, strlen(str2),
str3, strlen(str3));
return 0;
}

非中未定义的行为-null终止、相邻存储的C字符串

你为什么得到这个部分:

Worldhello 10
hello 5

而不是这个?

World 5
hello 5

答案是printf()打印字符,直到它碰到一个空字符,这是一个二进制零,通常写为''字符。而且,编译器恰好将包含hello的字符数组放置在包含World的字符数组之后。由于通过str[5]显式强制将str的大小设置为5,因此编译器无法在字符串末尾放置自动空字符。因此,由于hello恰好在World之后(保证是),并且printf()正在打印,直到它看到二进制零,它打印了World,没有看到终止的空字符,并继续在它之后的hello字符串中。这导致它打印Worldhello,然后只有当它看到hello之后的终止字符时才停止,哪个字符串被正确地终止。

此代码依赖于未定义的行为,这是一个错误。这是不能依赖的。但是,这就是这个案例的解释。

在64位Linux机器上使用gcc在线运行:在线GDB:非null终止的C字符串中的未定义行为

@Eric Postpischil给出了一个很好的答案,并在这里提供了更多的见解。

来自C标签wiki:

根据ISO 9899标准(最新版本,9899:2018,除非另有规定——也用c89、c99、c11等标记特定于版本的请求)的定义,此标记应用于与C语言有关的一般问题。

你问了一个"如何"关于这些文件都没有定义的问题,所以在C的上下文中答案是不确定的。你只能通过不确定的行为来体验这种现象。

它们是如何组合在一起的?

没有这样的要求,即这些变量中的任何一个都是";组合的";或者直接位于彼此之后;试图观察那是不明确的行为。它可能会在您的机器上偶尔出现故障,或者使用其他机器或编译器等情况下,对您来说不约而同地工作(无论这意味着什么)。这纯属巧合,不可信赖。

在某些情况下,上面的代码会将str与Worldhello一起分配,其余部分则按原样分配。

在未定义行为的情况下,对代码的功能进行声明是没有意义的,正如您已经注意到的,功能是不稳定的。

我发现他们有一些奇怪的行为。

如果您想防止不稳定的行为,请停止通过越界访问数组来调用未定义的行为(即导致strlen从数组末尾溢出)。

这些变量中只有一个可以安全地传递给strlen;您需要确保数组包含一个null终止符。

相关内容

最新更新