字符数组应如何用作字符串?

Lun*_*din 10 c string c-strings string-literals nul

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

#include <stdio.h>

int main (void)
{
  char str [5] = "hello";
  puts(str);
}
Run Code Online (Sandbox Code Playgroud)

为什么不起作用?

它可以用干净地编译gcc -std=c17 -pedantic-errors -Wall -Wextra


注意:对于在声明字符串时未能为NUL终止符分配空间而引起的问题,本帖子旨在用作规范的FAQ。

Lun*_*din 11

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

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

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

char str[6] = "hello";
Run Code Online (Sandbox Code Playgroud)

或者等效地,您可以编写5个字符加1个空终止符的自文档代码:

char str[5+1] = "hello";
Run Code Online (Sandbox Code Playgroud)

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

char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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


用gcc干净地编译

确实,甚至没有警告。这是由于C语言中的一个细微的细节/缺陷,它允许使用字符串文字初始化字符数组,该字符串文字包含的字符与数组中的空间一样多,然后静默丢弃空终止符(C17 6.7.9 / 15)。由于历史原因,该语言的行为方式故意如此,有关详细信息,请参见字符串初始化的gcc诊断不一致。还要注意,C ++在这里有所不同,并且不允许使用此技巧/缺陷。


Vla*_*cow 5

来自 C 标准(7.1.1 术语定义)

1字符串是由第一个空字符终止并包含第一个空字符的连续字符序列。有时使用术语多字节字符串来强调对字符串中包含的多字节字符进行特殊处理或避免与宽字符串混淆。指向字符串的指针是指向其初始(最低地址)字符的指针。字符串的长度是空字符之前的字节数,字符串的值是按顺序包含的字符的值的序列。

在这份声明中

char str [5] = "hello";
Run Code Online (Sandbox Code Playgroud)

字符串文字的"hello"内部表示形式如下

{ 'h', 'e', 'l', 'l', 'o', '\0' }
Run Code Online (Sandbox Code Playgroud)

所以它有 6 个字符,包括终止零。它的元素用于初始化字符数组,str该数组仅保留 5 个字符的空间。

当字符串文字的终止零不用作初始值设定项时,C 标准(与 C++ 标准相反)允许对字符数组进行此类初始化。

然而,结果字符数组str不包含字符串。

如果你希望数组包含一个字符串,你可以写

char str [6] = "hello";
Run Code Online (Sandbox Code Playgroud)

要不就

char str [] = "hello";
Run Code Online (Sandbox Code Playgroud)

在最后一种情况下,字符数组的大小由字符串文字的初始值设定项的数量(等于 6)确定。