为什么C ++支持结构体中的成员数组赋值,但通常不会呢?

我明白,不支持成员数组赋值,这样以下将不起作用:

int num1[3] = {1,2,3}; int num2[3]; num2 = num1; // "error: invalid array assignment" 

我只是接受了这个事实,认为语言的目的是提供一个开放式的框架,并让用户决定如何实现一些东西,如复制一个数组。

但是,下面的工作:

 struct myStruct {int num[3];}; myStruct struct1={{1,2,3}}; myStruct struct2; struct2 = struct1; 

数组num[3]是从struct1实例成员分配到struct1的实例。

为什么成员支持结构的数组赋值,但不是一般的?

编辑罗杰Pate的线程中的评论std ::string中的结构 – 复制/分配问题? 似乎指出了答案的大方向,但是我自己并不足以证实。

编辑2 :许多优秀的回应。 我select路德·布里塞特Luther Blissett )是因为我主要想知道行为背后的哲学或历史原理,但是詹姆斯·麦克奈利斯James McNellis )对相关规范文档的参考也是有用的。

这是我的承诺:

C语言的发展提供了一些在C:

我将尝试概述数组的事情:

C的先行者B和BCPL没有明确的数组types,声明如下:

 auto V[10] (B) or let V = vec 10 (BCPL) 

会声明V是一个(无types的)指针,它被初始化为指向内存的10个“字”的未使用区域。 B已经使用*作为指针解引用,并且有[]短手符号, *(V+i)意味着V[i] ,就像今天的C / C ++一样。 但是, V不是一个数组,它仍然是一个指向一些内存的指针。 当Dennis Ritchie试图用结构types扩展B时,这就造成了麻烦。 他希望数组成为结构的一部分,就像今天的C:

 struct { int inumber; char name[14]; }; 

但是使用B,BCPL作为指针的数组的概念,这将需要name字段包含一个指针,该指针必须在运行时初始化为结构内的14个字节的内存区域。 初始化/布局问题最终通过给予数组一个特殊的处理来解决:编译器会跟踪数组在结构,堆栈等中的位置,而实际上除了包含数组的expression式之外,实际上并不需要指向数据的指针。 这个处理允许几乎所有的B代码仍然运行,并且是“数组转换为指针,如果你看着它们”规则的来源。 这是一个兼容性破解,原来是非常方便的,因为它允许开放大小的数组等等

这里是我猜为什么数组不能分配:因为数组是B中的指针,你可以简单地写:

 auto V[10]; V=V+5; 

重新设定一个“arrays”。 这现在是没有意义的,因为数组variables的基数不再是左值。 所以这个分配是不允许的,这有助于抓住那些在声明的数组上进行重组的程序。 然后,这个概念卡住了:由于数组从来没有被devise成C类系统的第一类citate,所以他们大多被视为特殊的野兽,如果你使用它们就成为指针。 并且从某个angular度来看(忽略C数组是一个拙劣的黑客攻击),不允许数组赋值仍然有一定意义:开放数组或者数组函数参数被视为没有大小信息的指针。 编译器没有为其生成数组赋值的信息,并且出于兼容性的原因,指针赋值是必需的。 为声明的数组引入数组赋值会引入一些错误,虽然虚假赋值(是a = ba指针赋值还是元素拷贝)还有其他问题(如何通过值传递数组?)而没有真正解决问题 – 用memcpy显式的!

 /* Example how array assignment void make things even weirder in C/C++, if we don't want to break existing code. It's actually better to leave things as they are... */ typedef int vec[3]; void f(vec a, vec b) { vec x,y; a=b; // pointer assignment x=y; // NEW! element-wise assignment a=x; // pointer assignment x=a; // NEW! element-wise assignment } 

当1978年的C版本增加了结构赋值( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf )时,这并没有改变。 尽pipeC中的logging不同的types,但是在K&R早期不可能将它们赋值。您必须将它们以成员身份复制到memcpy中,并且只能将指针作为函数parameter passing给它们。 Assigment(和parameter passing)现在被简单地定义为结构的原始内存的memcpy,并且因为这不能破坏现有的代码,所以它很容易被使用。 作为一个意想不到的副作用,这隐含地引入了某种types的数组赋值,但是这在某个结构的某个地方是存在的,所以这并不能真正引入数组的使用方式。

关于赋值运算符,C ++标准说了以下(C ++ 03§5.17/ 1):

有几个赋值运算符… 都需要一个可修改的左值作为它们的左操作数

一个数组不是一个可修改的左值。

但是,对类types对象的赋值是专门定义的(§5.17/ 4):

赋值给一个类的对象由复制赋值操作符定义。

所以,我们看看为什么一个类的隐式声明的复制赋值操作符(§12.8/ 13):

类X的隐式定义的复制赋值运算符执行其子对象的成员赋值。 …每个子对象都以适合其types的方式进行分配:

– 如果子对象是一个数组,每个元素都以适合元素types的方式分配

所以,对于类types的对象,数组被正确地复制。 请注意,如果您提供用户声明的复制赋值运算符,则无法利用这一点,并且必须逐个复制数组。


C(C99§6.5.16/ 2)中的推理类似:

赋值运算符应该有一个可修改的左值作为其左操作数。

和§6.3.2.1/ 1:

可修改的左值是没有数组types的左值… [其他约束遵循]

在C中,赋值比C ++(§6.5.16.1/ 2)简单得多:

在简单赋值(=)中,右操作数的值被转换为赋值expression式的types,并replace左操作数指定的对象中存储的值。

对于结构types对象的赋值,左边和右边的操作数必须具有相同的types,所以右操作数的值只是复制到左边的操作数中。

在这个链接: http : //www2.research.att.com/~bs/bs_faq2.html有一个关于数组赋值的部分:

数组的两个基本问题是

  • 一个数组不知道自己的大小
  • 一个数组的名字转换成一个指向其第一个元素的最轻微的挑衅

我认为这是数组和结构之间的根本区别。 数组variables是自我知识有限的低级数据元素。 从根本上说,它是一块记忆和一个索引它的方法。

所以,编译器无法区分int a [10]和int b [20]。

结构,但是,不具有相同的歧义。

我知道,所有回答的人都是C / C ++的专家。 但我想,这是主要原因。

num2 = num1;

在这里你正试图改变数组的基地址,这是不允许的。

当然,struct2 = struct1;

在这里,对象struct1被分配给另一个对象。