为什么数组赋值操作不存在,但结构赋值在 C 语言中存在?


int a[10];
int b[10];
a = b; // illegal
typedef struct {
    int real;
    int imag;
    } complex;
complex c,d;
c = d; //legal

[我意识到 a 和 b 在第一种情况下是地址,但在第二种情况下是符号]

对于历史信息,这可能很有趣: http://cm.bell-labs.com/who/dmr/chist.html

在 B 中,声明数组将为数组留出内存,就像 C 一样,但为变量提供的名称用于定义指向数组的指针。Ritchie 在 C 语言中对此进行了更改,因此名称"is"是数组,但在使用时可以衰减为指针:

在今天的 C 中仍然存在的规则是数组类型的值 当它们出现在表达式中时,将转换为指向 组成数组的第一个对象。

本发明使大多数现有的B代码能够继续工作, 尽管语言语义发生了潜在的转变。为数不多的 为数组名称分配新值以调整其 起源——在B和BCPL中可能,在C中毫无意义——很容易修复。

如果在那个非常早期的阶段,Ritchie 定义了复制数组a = b,那么他试图从 B 移植到 C 的代码就不会那么容易修复。正如他所定义的那样,该代码将给出错误,他可以修复它。如果他让 C 复制数组,那么他会默默地改变代码的含义以复制数组,而不是重新拔插用于访问数组的名称。

仍然存在一个问题,"为什么在 40 年后没有添加此功能",但我认为这就是为什么它一开始就不存在的原因。这本来是努力实现的,而这种努力实际上会使C的早期版本变得更糟,从某种意义上说,将B和BCPL代码移植到C稍微困难一些。所以里奇当然没有这样做。

因为 C 说你不能,所以它说"可修改的左值是没有数组类型的左值,不没有不完整的类型",因此无法将数组分配给。

此外当您在值上下文中使用数组的名称(如 a = b; (时,名称ab的意思 &a[0]&b[0] .通常称为"衰减"到指针的数组。

但是,数组

不是指针,因此尝试使用指针分配数组是没有意义的。

主要原因当然是标准。在赋值运算符约束上,它说:

(C99, 6.5.16p2( "赋值运算符应有一个可修改的左操作数">

其中它将可修改的左值定义为

(C99, 6.3.2.1p1( "可修改的左值是没有数组类型的左值,[...]"。

因此不允许分配给数组。

但主要原因是在阵列拷贝被认为不适合硬件(旧的 PDP 系统(时的历史原因。在 C 的第一个版本中,也不允许分配结构类型对象。它后来被添加到语言中,但对于语言的许多部分的数组,需要更改以允许分配给数组。

首先要了解的是数组不是指针。 阅读 comp.lang.c FAQ 的第 6 节。 我等着。

好了,完成了? 不,回去读一遍。

太好了,谢谢。

一般来说,C 中的数组是二等公民。 有数组类型、数组对象,甚至数组值,但数组几乎总是通过指向其元素的指针进行操作。

这需要程序员做更多的工作(如您所见,您不能只分配数组(,但它也为您提供了更大的灵活性。 处理数组的程序通常需要处理不同大小的数组,甚至是直到执行时才能确定的大小。 即使允许数组分配,10 int 的数组和 20 int 的数组也是不同且不兼容的类型。 如果您有一个固定大小的数组,就像您问题中的代码一样,通常只有某些元素当前相关;您可能有一个 10 个元素的数组,但您目前只使用前 5 个元素。 逐个元素处理这样的数组可以更轻松地仅处理当前处于活动状态的元素(您必须自己跟踪(。

另一方面,对于结构,成员的数量和类型是在定义类型时确定的。 不能像对数组那样通过前进指针来遍历结构的成员,因为成员通常是不同类型的。 数组和结构是不同的东西,它们有不同的操作集,对它们有意义。

语言中有几个规则试图使它更容易做到这一点,即:

  • 在大多数(但不是所有(上下文中,数组表达式被隐式转换为指向数组第一个元素的指针。 例外情况是:
    • 当数组表达式是&(地址(运算符的操作数时;
    • 当它是sizeof的操作数时;和
    • 当它是初始值设定项中的字符串文本时,用于初始化数组对象。
  • 声明的数组参数(如 int func(char s[]); 中(调整为指针参数:int func(char *s);

(有人可能会争辩说,这些规则造成的混乱比它们防止的要多,但这就是语言的定义方式。

现在我想语言可以被定义,或者可以重新定义,以便在有意义的情况下允许数组赋值:

int a[10];
int b[10];
/* ... */
a = b; /* Why not? */.

也许甚至可以在不破坏现有代码的情况下进行这样的更改。 但这需要数组到指针转换规则的另一个特殊情况。 它仅在像 ab 这样的固定大小数组的情况下有用,尽管它们在入门编程练习中很常见,但在生产代码中并不常见。

数组名称是一个常量指针,因此您无法更改它指向的内容。

假设你的意思是最后一行的c = d是合法的,它只是将一个非常量变量复制到另一个非常量变量,这是完全合法的。

a 实际上是指向数组第一个元素的"指针",它是一个常量的"指针",所以你正在尝试分配一个 l-"指针"。

您可以通过以下方式实现您想要做的事情:

struct arraystruct
{
  int t[10];
};

struct arraystruct a,b;
a=b;

编辑:好吧,我忘了提到有一些例外情况,数组不应该被视为指针:

- 你可以使用 sizeof(

array(,但你不能使用 sizeof(指针(

-文字字符串数组

-a 和 &a 是相同的

这是因为您使用的数组类型是所谓的静态数组,即它的内存在堆栈上。如果您使用动态数组(带有指针(,则您的分配将是合法的(但可能会发生内存泄漏(。这将是一个浅层副本。

另请参阅静态数组与动态数组C++

最新更新