此帖子旨在作为规范的常见问题解答,用于以下问题:海报者在声明字符串时未能为空终止符分配空间。
有人告诉我,C语言中的字符串只是字符数组。因此,我尝试了以下操作,但它给出了奇怪的结果,例如垃圾输出或程序崩溃:
#include <stdio.h>
int main (void)
{
char str [5] = "hello";
puts(str);
}
为什么不起作用?
它以gcc -std=c17 -pedantic-errors -Wall -Wextra
干净地编译。
C字符串是一个以null终止符结尾的字符数组。
所有字符都有符号表值。空终止符是符号值0
(零)。它用于标记字符串的结尾。这是必需的,因为字符串的大小不会存储在任何地方。
因此,每次为字符串分配空间时,必须为空终止符包含足够的空间。您的示例不执行此操作,它仅为"hello"
的5个字符分配空间。正确的代码应为:
char str[6] = "hello";
或等效地,您可以编写5个字符加1个空终止符的自文档代码:
char str[5+1] = "hello";
在运行时为字符串动态分配内存时,您还需要为空终止符分配空间:
char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);
如果您没有在字符串的末尾附加一个空终止符,则期望该字符串的库函数将无法正常工作,并且您会收到“未定义行为”的错误,例如垃圾输出或程序崩溃。
在C中写空终止符的最常见方法是使用所谓的“八进制转义序列”,如下所示:'\0'
。这相当于写0
的100%,但是\
用作自记录代码,指出零明确表示是空终止符。诸如if(str[i] == '\0')
的代码将检查特定字符是否为空终止符。
[请注意,术语空终止符与空指针或NULL
宏无关!这可能会令人困惑-名称非常相似,但含义却截然不同。这就是为什么空终止符有时被称为带有一个L的NUL
,不要与NULL
或空指针混淆的原因。有关更多详细信息,请参见this SO question的答案。
您代码中的"hello"
被称为字符串文字。这将被视为只读字符串。 ""
语法意味着编译器将自动在字符串文字的末尾附加一个空终止符。因此,如果您打印出sizeof("hello")
,则将得到6,而不是5,因为您将获得包含空终止符的数组的大小。
它可以用gcc干净地编译
的确,甚至没有警告。这是由于C语言中的一个细微的细节/缺陷,它允许使用字符串文字初始化字符数组,该字符串文字包含的字符与数组中的空间一样多,然后静默丢弃空终止符(C17 6.7.9 / 15)。由于历史原因,该语言特意表现为这种行为,有关详细信息,请参见Inconsistent gcc diagnostic for string initialization。还请注意,C ++在这里有所不同,并且不允许使用此技巧/缺陷。
来自C标准(7.1.1术语定义)
1 字符串是一个连续的字符序列,由和终止包括第一个空字符。术语多字节字符串是有时用来代替强调字符串中包含多字节字符或避免混淆与宽的字符串。指向字符串的指针是指向其首字母的指针(最低寻址)字符。字符串的长度是空字符前面的字节,字符串的值是包含的字符的值顺序,按顺序。
在此声明中
char str [5] = "hello";
字符串文字"hello"
具有内部表示,如
{ 'h', 'e', 'l', 'l', 'o', '\0' }
也就是说,它有6个字符,包括结尾的零。它的元素用于初始化字符数组str
,该数组仅保留5个字符的空间。
[C标准(与C ++ STandard相反)允许在不使用字符串文字的结尾零作为初始化程序的情况下对字符数组进行此类初始化。
但是结果,字符数组str
不包含字符串。
如果您希望arrau包含您可以编写的字符串,则>]
char str [6] = "hello";
或只是
char str [] = "hello";
在最后一种情况下,由字符串文字taht的初始值设定项确定的字符数组的大小等于6。