ozm*_*zmo 82 c c++ arrays struct variable-assignment
我理解不支持成员分配数组,因此以下方法不起作用:
int num1[3] = {1,2,3};
int num2[3];
num2 = num1; // "error: invalid array assignment"
Run Code Online (Sandbox Code Playgroud)
我只是接受了这个事实,认为该语言的目的是提供一个开放式框架,并让用户决定如何实现诸如复制数组之类的东西.
但是,以下工作正常:
struct myStruct { int num[3]; };
struct myStruct struct1 = {{1,2,3}};
struct myStruct struct2;
struct2 = struct1;
Run Code Online (Sandbox Code Playgroud)
该数组num[3]是从其实例中成员分配struct1到其实例中的struct2.
为什么结构支持成员方式的数组,但一般情况下不支持?
编辑:Roger Pate 在结构中的线程std :: string中的注释- 复制/赋值问题?似乎指向答案的大致方向,但我不知道自己确认.
编辑2:许多出色的回应.我之所以选择Luther Blissett,是因为我对这种行为背后的哲学或历史原理很感兴趣,但James McNellis对相关规范文档的引用也很有用.
Nor*_*ame 43
这是我的看法:
C语言的开发提供了对C中数组类型演变的一些见解:
我将尝试概述数组的事情:
C的先行者B和BCPL没有明显的数组类型,声明如下:
auto V[10] (B)
or
let V = vec 10 (BCPL)
Run Code Online (Sandbox Code Playgroud)
将V声明为(无类型)指针,该指针被初始化为指向10"字"内存的未使用区域.乙已经使用*了指针引用,并有[] 速记符号,*(V+i)意味着V[i],/ C++今天刚刚在C.但是,V它不是一个数组,它仍然是一个必须指向某个内存的指针.当Dennis Ritchie试图用结构类型扩展B时,这会带来麻烦.他希望数组成为结构的一部分,就像今天的C一样:
struct {
int inumber;
char name[14];
};
Run Code Online (Sandbox Code Playgroud)
但是,使用B,BCPL数组作为指针的概念,这将要求name字段包含一个指针,该指针必须在运行时初始化为结构中14字节的内存区域.初始化/布局问题最终通过为数组提供特殊处理来解决:编译器将跟踪数组在结构中,堆栈上的位置等,而实际上不需要指向数据的指针来实现,除了涉及数组的表达式.这种处理几乎允许所有B代码仍然运行,并且是"如果你看它们就转换为指针的数组"规则的来源.这是一个兼容性的黑客,结果非常方便,因为它允许开放大小的阵列等.
这里是我的猜测为什么不能分配数组:由于数组是B中的指针,你可以简单地写:
auto V[10];
V=V+5;
Run Code Online (Sandbox Code Playgroud)
改变一个"数组".这现在毫无意义,因为数组变量的基数不再是左值.所以这个分配是不允许的,这有助于捕获在声明的数组上进行这种变基的少数程序.然后这个概念卡住了:因为数组从来没有被设计为C类型系统的第一类城市化,所以它们大多被视为特殊的野兽,如果你使用它们就会成为指针.从某个角度来看(忽略了C阵列是一个拙劣的黑客攻击),禁止数组赋值仍然有一定道理:开放数组或数组函数参数被视为没有大小信息的指针.编译器没有为它们生成数组赋值的信息,并且出于兼容性原因需要指针赋值.
/* 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
}
Run Code Online (Sandbox Code Playgroud)
当1978年的C修订版添加了结构分配(http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf)时,这没有改变.即使记录是 C 中的不同类型,也不可能在早期的K&R C中分配它们.您必须使用memcpy将它们成员复制,并且您只能将指针作为函数参数传递给它们.Assigment(和参数传递)现在简单地定义为struct的原始内存的memcpy,因为这不能破坏现有的代码,所以它很容易被引用.作为一种意想不到的副作用,这隐含地引入了某种类型的数组赋值,但是它在结构中的某处发生了,所以这不能真正引入数组使用方式的问题.
Jam*_*lis 28
关于赋值运算符,C++标准如下(C++03§5.17/ 1):
有几个赋值运算符... 都需要一个可修改的左值作为它们的左操作数
数组不是可修改的左值.
但是,特别定义了对类类型对象的赋值(§5.17/ 4):
对类的对象的赋值由复制赋值运算符定义.
因此,我们期待看一个类的隐式声明的复制赋值运算符(§12.8/ 13):
类X的隐式定义的复制赋值运算符执行其子对象的成员分配....每个子对象都以适合其类型的方式分配:
...
- 如果子对象是一个数组,则以适合于元素类型的方式分配每个元素
...
因此,对于类类型对象,可以正确复制数组.请注意,如果您提供用户声明的复制赋值运算符,则无法利用此操作,并且您必须逐个元素地复制数组.
推理在C(C99§6.5.16/ 2)中类似:
赋值运算符应具有可修改的左值作为其左操作数.
并且§6.3.2.1/ 1:
一个可修改的左值是一个没有数组类型的左值... [其他约束如下]
在C中,赋值比C++(§6.5.16.1/ 2)简单得多:
在简单赋值(=)中,右操作数的值将转换为赋值表达式的类型,并替换存储在左操作数指定的对象中的值.
对于struct-type对象的赋值,左右操作数必须具有相同的类型,因此右操作数的值只是复制到左操作数中.