首页 > 解决方案 > 字符数组应该如何用作字符串?

问题描述

我知道 C 中的字符串只是字符数组。所以我尝试了下面的代码,但它给出了奇怪的结果,例如垃圾输出或程序崩溃:

#include <stdio.h>

int main (void)
{
  char str [5] = "hello";
  puts(str);
}

为什么这不起作用?

它与gcc -std=c17 -pedantic-errors -Wall -Wextra.


注意:这篇文章旨在用作规范的常见问题解答,用于解决在声明字符串时未能为 NUL 终止符分配空间而导致的问题。

标签: cstringc-stringsstring-literalsnul

解决方案


AC 字符串是以空终止符结尾的字符数组。

所有字符都有一个符号表值。空终止符是符号值0(零)。它用于标记字符串的结尾。这是必要的,因为字符串的大小没有存储在任何地方。

因此,每次为字符串分配空间时,都必须为空终止符包含足够的空间。您的示例没有这样做,它只为 5 个字符分配空间"hello"。正确的代码应该是:

char str[6] = "hello";

或者等效地,您可以为 5 个字符加上 1 个空终止符编写自记录代码:

char str[5+1] = "hello";

但是您也可以使用它并让编译器进行计数并选择大小:

char str[] = "hello"; // Will allocate 6 bytes automatically

在运行时为字符串动态分配内存时,还需要为空终止符分配空间:

char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);

如果您不在字符串末尾附加空终止符,则期望字符串的库函数将无法正常工作,并且您将获得“未定义行为”错误,例如垃圾输出或程序崩溃。

在 C 中编写空终止符的最常见方法是使用所谓的“八进制转义序列”,如下所示'\0':这 100% 等同于 writing 0,但\作为自文档代码来声明零明确表示为空终止符。诸如if(str[i] == '\0')将检查特定字符是否为空终止符的代码。

NULL请注意,术语空终止符与空指针或宏无关!这可能会令人困惑——名称非常相似,但含义却截然不同。这就是为什么空终止符有时被称为一个 L,不要与空指针NUL混淆。有关更多详细信息,请参阅此 SO 问题NULL的答案。

您的"hello"代码中的 称为字符串文字。这将被视为只读字符串。该""语法意味着编译器将自动在字符串文字的末尾附加一个空终止符。所以如果你打印出来,sizeof("hello")你会得到 6,而不是 5,因为你得到了包含空终止符的数组的大小。


它用 gcc 干净地编译

的确,连警告都没有。这是因为 C 语言中有一个细微的细节/缺陷,它允许使用包含与数组中的空间一样多的字符的字符串文字来初始化字符数组,然后默默地丢弃空终止符(C17 6.7.9/ 15)。由于历史原因,该语言故意这样做,有关详细信息,请参阅Inconsistent gcc diagnostic for string initialization。另请注意,C++ 在这里有所不同,并且不允许使用此技巧/缺陷。


推荐阅读