c中数组初始化的背景会发生什么?

The*_*eer 5 c arrays initialization variable-assignment

鉴于这是合法的

uint8_t bytes[4] = { 1, 2, 3, 4 };
Run Code Online (Sandbox Code Playgroud)

这不是:

uint8_t bytes2[4];
bytes2 = { 1, 2, 3, 4 };
Run Code Online (Sandbox Code Playgroud)

什么{ 1, 2, 3, 4 }代表?

假设它既不是右值也不是左值.一个预处理器代码糖果扩展到什么?

Sou*_*osh 8

类似的语法{1,2,3,4};称为大括号括号初始化列表,它是一个初始化程序.它只能用于初始化(对于数组类型).

引用C11,章节§6.7.9

  • P11

标量的初始值设定项应为单个表达式,

[数组不是标量类型,因此不适用于我们]

  • P14

字符类型数组可以由字符串文字或UTF-8字符串文字初始化,可选地用大括号括起来.

[我们这里没有使用字符串文字,因此也不适用于我们]

  • P16

否则,具有聚合或联合类型的对象的初始值设定项应该是元素或命名成员的大括号括起来的初始值设定项列表.

[这是我们感兴趣的情况]

和,P17,

每个大括号括起的初始化列表都有一个关联的当前对象.当没有指定时,根据当前对象的类型按顺序初始化当前对象的子对象:增加下标顺序的数组元素,声明顺序中的结构成员,以及union的第一个命名成员.[... ]

因此,这里,括号括起来的列表中的值不直接"赋值"给数组,它们用于初始化数组的各个成员.

OTOH是一种数组类型,不是可修改的左值,因此无法分配.换句话说,数组变量不能用作赋值运算符的LHS.

为了详细说明,从C11章§6.5.16

赋值运算符应具有可修改的左值作为其左操作数.


Lun*_*din 7

{1,2,3,4}是一个初始化列表,一个特殊的语法标记,只能在声明数组的行上使用.

这完全由C标准语法规范.它背后没有特别的理由,这就是语言的定义方式.在C语法中,数组不能分配,也不能通过赋值复制.

但是,您可以通过多种方式避免语法限制,一次覆盖所有值.最简单的方法是创建一个临时数组和memcpy:

uint8_t tmp[] = {5,6,7,8};
memcpy(bytes, tmp, sizeof bytes);
Run Code Online (Sandbox Code Playgroud)

或者,使用复合文字:

memcpy(bytes, (uint8_t[]){5,6,7,8}, sizeof bytes);
Run Code Online (Sandbox Code Playgroud)

如果对特定应用程序有意义,您还可以将数组包装在结构中以绕过语法限制:

typedef struct
{
  uint8_t data [4];
} array_t;

...

array_t bytes = { .data = {1,2,3,4} };
array_t tmp   = { .data = {5,6,7,8} };
bytes = tmp; // works just fine, structs can be copied this way
Run Code Online (Sandbox Code Playgroud)

  • 请不要将其他部分答案复制到我的部分,特别是在没有给出归属的情况下 - 使文本看起来好像被盗了.发布几个相互补充的答案是完全没问题的.只需投票选出所有有用的答案,并将最有用的答案标记为已接受. (2认同)

小智 5

初始化和分配是根本不同的事情.至于语言C,你只需要接受它们被区分的事实,但当然,有一个技术原因它是这样定义的:

在许多系统中,您可以在可执行文件中包含数据段.这段可以读/写,并给出一个初始化的数组

uint8_t foo[] = {1, 2, 3, 4}; // assume this has static storage duration
Run Code Online (Sandbox Code Playgroud)

编译器可以决定将这个确切的字节序列直接输出到您的可执行文件中.因此,没有代码可以完成任务,当程序启动时,数据已经存在于内存中.


OTOH,数组不能分配给(只有他们的个人成员).这就是C的定义方式,有时候很不幸.