为什么 C 和 C++ 支持结构内数组的成员分配,但通常不支持? [英] Why do C and C++ support memberwise assignment of arrays within structs, but not generally?

查看:20
本文介绍了为什么 C 和 C++ 支持结构内数组的成员分配,但通常不支持?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道不支持数组的成员分配,因此以下将不起作用:

I understand that memberwise assignment of arrays is not supported, such that the following will not work:

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

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

I just accepted this as fact, figuring that the aim of the language is to provide an open-ended framework, and let the user decide how to implement something such as the copying of an array.

但是,以下确实有效:

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

数组num[3] 是从struct1 中的实例到struct2 中的实例按成员分配的.

The array num[3] is member-wise assigned from its instance in struct1, into its instance in struct2.

为什么结构支持按成员分配数组,但一般不支持?

Why is member-wise assignment of arrays supported for structs, but not in general?

编辑:Roger Pate 在线程 std::string in struct - Copy/assignment issues? 似乎指向了答案的大致方向,但我自己了解的还不够多.

edit: Roger Pate's comment in the thread std::string in struct - Copy/assignment issues? seems to point in the general direction of the answer, but I don't know enough to confirm it myself.

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

edit 2: Many excellent responses. I choose Luther Blissett's because I was mostly wondering about the philosophical or historical rationale behind the behavior, but James McNellis's reference to the related spec documentation was useful as well.

推荐答案

这是我的看法:

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

The Development of the C Language offers some insight in the evolution of the array type in C:

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

I'll try to outline the array thing:

C 的先驱 B 和 BCPL 没有不同的数组类型,声明如下:

C's forerunners B and BCPL had no distinct array type, a declaration like:

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

将声明 V 为(无类型的)指针,该指针被初始化为指向内存的 10 个字"的未使用区域.B 已经将 * 用于指针解引用并使用 [] 速记符号,*(V+i) 表示 V[i],就像今天的 C/C++ 一样.然而,V 不是一个数组,它仍然是一个必须指向某个内存的指针.当 Dennis Ritchie 尝试使用结构类型扩展 B 时,这引起了麻烦.他希望数组成为结构的一部分,就像今天的 C 一样:

would declare V to be a (untyped) pointer which is initialized to point to an unused region of 10 "words" of memory. B already used * for pointer dereferencing and had the [] short hand notation, *(V+i) meant V[i], just as in C/C++ today. However, V is not an array, it is still a pointer which has to point to some memory. This caused trouble when Dennis Ritchie tried to extend B with struct types. He wanted arrays to be part of the structs, like in C today:

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

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

But with the B,BCPL concept of arrays as pointers, this would have required the name field to contain a pointer which had to be initialized at runtime to a memory region of 14 bytes within the struct. The initialization/layout problem was eventually solved by giving arrays a special treatment: The compiler would track the location of arrays in structures, on the stack etc. without actually requiring the pointer to the data to materialize, except in expressions which involve the arrays. This treatment allowed almost all B code to still run and is the source of the "arrays convert to pointer if you look at them" rule. It is a compatiblity hack, which turned out to be very handy, because it allowed arrays of open size etc.

我猜为什么数组不能被赋值:因为数组是 B 中的指针,你可以简单地写:

And here's my guess why array can't be assigned: Since arrays were pointers in B, you could simply write:

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

重新定义一个数组".这现在毫无意义,因为数组变量的基数不再是左值.所以这种分配是不允许的,这有助于捕获少数程序在声明的数组上执行此重新设置.然后这个概念就卡住了:因为数组从来没有被设计为 C 类型系统的一等引用,它们大多被视为特殊的野兽,如果你使用它们就会变成指针.从某个角度来看(它忽略了 C 数组是一个拙劣的黑客),不允许数组赋值仍然有一定的意义:开放数组或数组函数参数被视为没有大小信息的指针.编译器没有为它们生成数组分配的信息,并且出于兼容性原因需要指针分配.为已声明的数组引入数组赋值会引入错误,尽管虚假赋值(是 a=ba 指针赋值还是按元素复制?)和其他麻烦(如何按值传递数组?),而没有实际解决问题 - 只需制作所有内容用 memcpy 明确!

to rebase an "array". This was now meaningless, because the base of an array variable was not a lvalue anymore. So this assigment was disallowed, which helped to catch the few programs that did this rebasing on declared arrays. And then this notion stuck: As arrays were never designed to be first class citized of the C type system, they were mostly treated as special beasts which become pointer if you use them. And from a certain point of view (which ignores that C-arrays are a botched hack), disallowing array assignment still makes some sense: An open array or an array function parameter is treated as a pointer without size information. The compiler doesn't have the information to generate an array assignment for them and the pointer assignment was required for compatibility reasons. Introducing array assignment for the declared arrays would have introduced bugs though spurious assigments (is a=b a pointer assignment or an elementwise copy?) and other trouble (how do you pass an array by value?) without actually solving a problem - just make everything explicit with 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,并且由于这不能破坏现有代码,因此很容易被采用.作为一个意想不到的副作用,这隐含地引入了某种数组分配,但这发生在结构内部的某个地方,因此这不会真正引入数组使用方式的问题.

This didn't change when a revision of C in 1978 added struct assignment ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Even though records were distinct types in C, it was not possible to assign them in early K&R C. You had to copy them member-wise with memcpy and you could pass only pointers to them as function parameters. Assigment (and parameter passing) was now simply defined as the memcpy of the struct's raw memory and since this couldn't break exsisting code it was readily adpoted. As a unintended side effect, this implicitly introduced some kind of array assignment, but this happended somewhere inside a structure, so this couldn't really introduce problems with the way arrays were used.

这篇关于为什么 C 和 C++ 支持结构内数组的成员分配,但通常不支持?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆