Dan*_*Dan 55 c string-literals array-initialization
#include <stdio.h>
int main() {
char a = 5;
char b[2] = "hi"; // No explicit room for `\0`.
char c = 6;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
每当我们编写一个用双引号括起来的字符串时,C 都会自动为我们创建一个字符数组,其中包含该字符串,并以 \0 字符结尾 http://www.eskimo.com/~scs/cclass/notes/sx8。 html
在上面的示例中,b只有 2 个字符的空间,因此空终止字符没有位置可放置,但编译器正在重新组织内存存储指令,以便将a和c存储b在内存中,以便为 a 腾出\0空间数组的末尾。
这是预期的还是我遇到了未定义的行为?
dbu*_*ush 50
char如果数组至少足够大以容纳字符串中除空终止符之外的所有字符,则允许使用字符串初始化数组。
C 标准第 6.7.9p14 节对此进行了详细说明:
\n\n\n字符类型数组可以由字符串\n文字或 UTF\xe2\x88\x928 字符串文字初始化,可以选择用大括号括起来。\n字符串文字的连续字节(包括终止 null\n字符,如果有空间或如果该数组的大小未知)\n初始化该数组的元素。
\n
但是,这也意味着您不能将数组视为字符串,因为它不是以 null 结尾的。正如所写,由于您没有对执行任何字符串操作b,因此您的代码没有问题。
你不能做的是使用太长的字符串进行初始化,即:
\nchar b[2] = "hello";\nRun Code Online (Sandbox Code Playgroud)\n因为这给出的初始值设定项超出了数组所能容纳的范围,并且违反了约束条件。第 6.7.9p2 节规定如下:
\n\n\n任何初始化程序都不得尝试为未包含在正在初始化的实体中的对象提供值。
\n
如果您要像这样声明并初始化数组:
\nchar b[] = "hi"; \nRun Code Online (Sandbox Code Playgroud)\n那么b将是一个大小为 3 的数组,该数组足够大以容纳字符串常量中的两个字符加上终止空字节,使得b一个字符串。
总结一下:
\n如果数组有固定大小:
\n如果数组没有明确的大小,则数组的大小将调整为容纳字符串常量加上终止空字节。
\nSte*_*mit 35
\n\n每当我们编写一个用双引号括起来的字符串时,C 都会自动为我们创建一个字符数组,其中包含该字符串,并以 \\0 字符结尾。
\n
在这种情况下,这些注释具有轻微的误导性。我必须更新它们。
\n当你写类似的东西时
\nchar *p = "Hello";\nRun Code Online (Sandbox Code Playgroud)\n或者
\nprintf("world!\\n");\nRun Code Online (Sandbox Code Playgroud)\nC 会自动为您创建一个大小合适的字符数组,其中包含字符串,并以字符结尾\\0。
然而,在数组初始值设定项的情况下,情况略有不同。当你写的时候
\nchar b[2] = "hi";\nRun Code Online (Sandbox Code Playgroud)\n该字符串只是您正在创建的数组的初始值设定项。因此您可以完全控制尺寸。有几种可能:
\nchar b0[] = "hi"; // compiler infers size\nchar b1[1] = "hi"; // error\nchar b2[2] = "hi"; // No terminating 0 in the array. (Illegal in C++, BTW)\nchar b3[3] = "hi"; // explicit size matches string literal\nchar b4[10] = "hi"; // space past end of initializer is always zero-initialized\nRun Code Online (Sandbox Code Playgroud)\n对于b0,您没有指定大小,因此编译器使用字符串初始值设定项来选择正确的大小,即 3。
对于b1,您指定了一个大小,但它太小,因此编译器应该给您一个错误。
对于b2,这是您询问的情况,您指定的大小对于字符串初始值设定项中的显式字符来说刚刚足够大,但不是终止的\\0。这是一个特殊情况。这是合法的,但是你最终得到的b2不是一个正确的空终止字符串。由于它充其量是不寻常的,因此编译器可能会向您发出警告。有关此案例的更多信息,请参阅此问题。
对于b3,您指定了一个恰到好处的大小,因此您可以在精确大小的数组中获得正确的字符串,就像 一样b0。
对于b4,您指定的大小太大,但这没有问题。数组中最终会出现超出终止符的额外空间\\0。(事实上,这个额外的空间也会被填满\\0。)这个额外的空间可以让你安全地做类似的事情strcat(b4,\xc2\xa0",\xc2\xa0wrld!")。
不用说,大多数时候您想使用表单b0。计算字符是乏味且容易出错的。正如 Brian Kernighan(C 语言的创建者之一)在这种情况下所写的那样,“让计算机来做那些肮脏的工作。”
还有一件事。你写了:
\n\n\n然而编译器正在重新组织内存存储指令,以便将
\na和c存储b在内存之前,以便为\\0数组末尾的 a 腾出空间。
我不知道那里发生了什么,但可以肯定地说编译器并没有试图“为”腾出空间\\0。编译器可以而且经常按照自己难以理解的内部顺序存储变量,既不匹配您声明它们的顺序,也不匹配字母顺序,也不匹配您可能想到的任何其他顺序。如果在你的编译器数组下面b有额外的空间,其中确实包含一个\\0好像终止字符串的东西,这可能基本上是随机的机会,而不是因为编译器试图对你友善并帮助printf("%s\\n", b)更好地定义之类的东西。(在我尝试过的两个编译器下,printf("%s\\n", b)打印了hi^E和hi ??,清楚地显示了尾随随机垃圾的存在,正如预期的那样。)
你的问题有两点。
字符串字面量。字符串文字(即用双引号括起来的内容)始终是正确的空字符终止字符串。
char *p = "ABC"; // p references null character terminated string
Run Code Online (Sandbox Code Playgroud)
字符数组只能容纳尽可能多的元素,因此如果您尝试使用三个元素字符串文字初始化两个元素数组,则只会先写入两个元素。所以数组不会包含以空字符结尾的 C 字符串
char p[2] = "AB"; // p is not a valid C string.
Run Code Online (Sandbox Code Playgroud)