c++ - 为什么 C 和 C++ 支持结构内数组的成员赋值,但通常不支持?

标签 c++ c arrays variable-assignment struct

我了解不支持按成员分配数组,因此以下操作将不起作用:

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

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

但是,以下内容确实有效:

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

数组 num[3] 从其在 struct1 中的实例按成员分配到其在 struct2 中的实例。

为什么结构支持按成员赋值数组,但通常不支持?

编辑:Roger Pate在话题中的评论std::string in struct - Copy/assignment issues?似乎指出了答案的大致方向,但我自己还不足以确认。

edit 2:许多优秀的回复。我选择 Luther Blissett 是因为我主要想知道行为背后的哲学或历史基本原理,但是 James McNellis 对相关规范文档的引用也很有用.

最佳答案

这是我的看法:

C 语言的发展提供了一些关于 C 中数组类型演变的见解:

我将尝试概述数组的内容:

C 的前身 B 和 BCPL 没有明确的数组类型,声明如下:

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

会将 V 声明为一个(无类型)指针,该指针被初始化为指向内存中 10 个“字”的未使用区域。 B 已经使用 * 进行指针取消引用并使用了 [] 简写符号,*(V+i) 表示 V[i ],就像今天的 C/C++ 一样。但是,V 不是一个数组,它仍然是一个必须指向某个内存的指针。当 Dennis Ritchie 试图用结构类型扩展 B 时,这引起了麻烦。他希望数组成为结构的一部分,就像今天在 C 中一样:

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

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

下面是我对数组不能赋值的猜测:因为数组是 B 中的指针,你可以简单地写:

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

rebase 一个“数组”。这现在毫无意义,因为数组变量的基数不再是左值。所以这个赋值是不允许的,这有助于捕获少数在声明的数组上执行此 rebase 的程序。。然后这个概念就陷入了困境:由于数组从未被设计为 C 类型系统的一等公民,它们大多被视为特殊的野兽,如果你使用它们就会变成指针。从某种角度来看(忽略了 C 数组是拙劣的 hack),不允许数组赋值仍然有一定的意义:开放数组或数组函数参数被视为没有大小信息的指针。编译器没有为它们生成数组赋值的信息,并且出于兼容性原因需要指针赋值。为已声明的数组引入数组赋值会通过虚假赋值(a=b 是指针赋值还是元素复制?)和其他麻烦(如何按值传递数组?)引入错误,而没有真正解决问题 - 只是使一切用 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) 时,这并没有改变。尽管记录在 C 中不同的类型,但在早期的 K&R C 中不可能对它们进行分配。您必须使用 memcpy 按成员方式复制它们,并且您只能将指向它们的指针作为函数参数传递。赋值(和参数传递)现在被简单地定义为结构原始内存的 memcpy,并且由于这不会破坏现有代码,因此很容易被采用。作为一个意想不到的副作用,这隐含地引入了某种数组赋值,但这发生在结构内部的某个地方,所以这并不能真正引入数组使用方式的问题。

关于c++ - 为什么 C 和 C++ 支持结构内数组的成员赋值,但通常不支持?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64698678/

相关文章:

java - 从数组中获取随机值

Java:从 ArrayList 中获取所有第一个条目

c++ - 带有第二个参数=0 的 eventfd_write() 不起作用?

c++ - 如何在不知道最初输入多少值的情况下将整数存储到数组

c - 从字符串 C 中读取一些数据

c++ - ( float )在 c/c++ 中

c - 如何将逻辑转换为指针逻辑

c - 打印 C 中结构数组中每个结构成员的函数

c++ - 由于签名差异而调用了错误的子类函数

c++ - 使用 `std::shared_ptr` 时有效地节省内存/编程