分配要求编译器知道要分配的任何内容的类型和大小。所以表格的赋值
a = b;
要求编译器知道a 和b 的类型。如果类型相同(例如a 和b 的类型都是int),那么编译器可以通过它认为最有效的任何指令简单地将b 复制到a。如果类型不同,但允许隐式提升或类型转换,则在进行提升后也可以进行赋值。例如,如果a 是long 类型,而b 是short 类型,则b 将被隐式提升为long,并且该提升的结果存储在a 中。
这不适用于数组,因为数组的大小(计算为其元素的大小乘以元素的数量)不一定是已知的。一个编译单元(又名源文件)可能有一个声明(可能通过包含一个头文件)
extern int a[];
extern int b[];
void some_func()
{
a = b;
}
它告诉编译器a 和b 是int 的数组,但它们将由另一个编译单元定义(包括给它们一个大小)。然后另一个编译单元可能会这样做;
extern int a[];
int a[] = {3,1,4,2,3}; /* definition of a */
第三个编译单元可以类似地将b定义为27个元素的数组。
一旦将目标文件链接到单个可执行文件中,a 和 b 在所有编译单元中的用法都是相关联的,并且对它们的所有操作都引用相同的定义。
这个问题的出现是因为单独编译模型是 C 的核心特性。所以编译器在咀嚼上面的第一个编译单元时,没有关于数组大小的信息,因为它看不到其他的编译单元,并且需要在不参考它们的情况下成功或诊断错误。由于没有关于第一个编译单元可用的任一数组中元素数量的信息,因此无法计算出从一个数组复制到另一个数组的元素数量。在 C 中对此的处理是赋值 a = b 是函数 some_func() 中的可诊断错误。
有替代方法(并且一些其他编程语言以不同方式处理此类情况),但它们通常与其他权衡相关。
这些注意事项通常不会影响struct 类型,因为它们的大小在编译时是已知的。因此,如果 a 和 b 属于相同的 struct 类型,则分配 a = b 是可能的 - 并且可以通过(例如)调用 memcpy() 来实现。
注意:我在上面的解释中故意过度简化,例如不考虑具有灵活数组成员的结构的情况(来自 C99)。在不改变核心考虑的情况下,讨论此类案例会使上述讨论更加复杂。