Frage Warum unterstützt C ++ die elementweise Zuweisung von Arrays innerhalb von Strukturen, aber nicht generell?


Ich verstehe, dass die elementweise Zuweisung von Arrays nicht unterstützt wird, sodass Folgendes nicht funktioniert:

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

Ich habe dies einfach als Tatsache akzeptiert und festgestellt, dass das Ziel der Sprache darin besteht, ein Framework mit offenem Ende bereitzustellen und den Benutzer entscheiden zu lassen, wie etwas wie das Kopieren eines Arrays implementiert werden soll.

Folgendes funktioniert jedoch:

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

Das Array num[3] wird Mitglied weise von seiner Instanz in zugewiesen struct1, in seine Instanz in struct2.

Warum wird die elementweise Zuweisung von Arrays für Strukturen unterstützt, aber nicht allgemein?

bearbeiten: Roger Pate's Kommentar im Thread std :: string in struct - Probleme beim Kopieren / Zuordnen? scheint in die allgemeine Richtung der Antwort zu zeigen, aber ich weiß nicht genug, um es selbst zu bestätigen.

bearbeite 2: Viele ausgezeichnete Antworten. ich wähle Luther Blissettweil ich mich hauptsächlich über die philosophischen oder historischen Gründe für dieses Verhalten Gedanken gemacht habe, aber James McNellisDer Verweis auf die zugehörige Spezifikationsdokumentation war ebenfalls hilfreich.


75
2017-08-09 03:10


Ursprung


Antworten:


Hier ist meine Meinung dazu:

Die Entwicklung der C-Sprache bietet einen Einblick in die Entwicklung des Array-Typs in C:

Ich werde versuchen, die Array-Sache zu skizzieren:

C's Vorläufer B und BCPL hatten keinen eindeutigen Array-Typ, eine Deklaration wie:

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

würde V als einen (untypisierten) Zeiger deklarieren, der initialisiert wird, um auf einen unbenutzten Bereich von 10 "Wörtern" des Speichers zu zeigen. B schon benutzt * für Zeiger Dereferenzierung und hatte die []Kurzschreibweise, *(V+i) gemeint V[i], genau wie in C / C ++ heute. Jedoch, V ist kein Array, es ist immer noch ein Zeiger, der auf etwas Speicher zeigen muss. Dies verursachte Probleme, als Dennis Ritchie versuchte, B mit Strukturtypen zu erweitern. Er wollte, dass Arrays Teil der Strukturen sind, wie in C heute:

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

Aber mit dem B, BCPL - Konzept der Arrays als Zeiger hätte dies die name Feld, um einen Zeiger zu enthalten, der sein musste zur Laufzeit initialisiert zu einem Speicherbereich von 14 Bytes innerhalb der Struktur. Das Initialisierungs- / Layout-Problem wurde schließlich dadurch gelöst, dass Arrays eine spezielle Behandlung erhielten: Der Compiler verfolgte die Position von Arrays in Strukturen, auf dem Stack usw., ohne dass der Zeiger auf die Daten materialisiert werden musste, außer in Ausdrücken, die die Arrays beinhalteten. Diese Behandlung erlaubt fast alle B-Code noch zu laufen und ist die Quelle der "Arrays konvertieren in Zeiger, wenn Sie sie betrachten" Regel. Es ist ein Kompatibilitäts-Hack, der sich als sehr nützlich erwies, weil er Arrays von offener Größe usw. erlaubte.

Und hier ist meine Vermutung, warum Array nicht zugewiesen werden kann: Da Arrays Zeiger in B waren, könnten Sie einfach schreiben:

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

um ein "Array" neu zu erstellen. Dies war jetzt bedeutungslos, weil die Basis einer Array-Variablen kein Lvalue mehr war. Diese Zuweisung wurde also nicht zugelassen, was dazu beitrug, dass die wenigen Programme, die diese Neuausrichtung durchführten, auffielen auf deklarierten Arrays. Und dann klebte dieser Gedanke: Da Arrays nie so designt wurden, dass sie vom C-Typ-System in erster Linie zitiert werden, wurden sie meist als spezielle Bestien behandelt, die zu Pointern werden, wenn man sie benutzt. Und von einem bestimmten Standpunkt (der ignoriert, dass C-Arrays ein verpfuschter Hack sind), macht die Ablehnung der Array-Zuweisung immer noch Sinn: Ein offenes Array oder ein Array-Funktionsparameter wird als ein Zeiger ohne Größeninformation behandelt. Der Compiler verfügt nicht über die Informationen zum Generieren einer Array-Zuweisung für sie und die Zeigerzuweisung wurde aus Kompatibilitätsgründen benötigt. Die Einführung der Array-Zuweisung für die deklarierten Arrays hätte Fehler durch falsche Zuweisung (ist a = ba Zeigerzuweisung oder eine elementweise Kopie?) Und andere Probleme (wie übergeben Sie ein Array nach Wert?) Eingeführt, ohne tatsächlich ein Problem zu lösen - machen Sie einfach alles explizit mit 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
}

Dies änderte sich nicht, als 1978 eine Revision von C die Strukturzuweisung ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Obwohl Aufzeichnungen wurden unterschiedliche Typen in C, es war nicht möglich, sie in frühen K & R C zuzuordnen. Sie mussten sie mit Memcpy memberweise kopieren und Sie konnten nur Zeiger als Funktionsparameter übergeben. Assigment (und Parameterübergabe) wurde jetzt einfach als das Memcpy des root-Speichers der Struktur definiert, und da dieser vorhandenen Code nicht durchbrechen konnte, wurde er leicht adoptiert. Als unbeabsichtigter Nebeneffekt führte dies implizit zu einer Art von Array-Zuweisung, aber dies geschah irgendwo innerhalb einer Struktur, so dass dies nicht wirklich zu Problemen bei der Art und Weise führen konnte, wie Arrays verwendet wurden.


38
2017-08-09 07:16



Bezüglich der Zuweisungsoperatoren sagt der C ++ - Standard folgendes aus (C ++ 03 §5.17 / 1):

Es gibt mehrere Zuweisungsoperatoren ... alle benötigen einen modifizierbaren L-Wert als ihren linken Operanden

Ein Array ist kein modifizierbarer L-Wert.

Die Zuordnung zu einem Objekt vom Klassentyp ist jedoch speziell definiert (§5.17 / 4):

Die Zuordnung zu Objekten einer Klasse wird vom Kopierzuweisungsoperator definiert.

Also schauen wir uns an, was der implizit deklarierte Kopierzuweisungsoperator für eine Klasse macht (§12.8 / 13):

Der implizit definierte Kopierzuweisungsoperator für die Klasse X führt die elementweise Zuweisung seiner Unterobjekte durch. ... Jedes Unterobjekt wird seinem Typ entsprechend zugeordnet:
  ...
  - Wenn das Unterobjekt ein Array ist, wird jedes Element entsprechend dem Elementtyp zugewiesen
  ...

Bei einem Objekt vom Typ class werden Arrays also korrekt kopiert. Beachten Sie, dass Sie keinen Vorteil daraus ziehen können, wenn Sie einen vom Benutzer deklarierten Kopierzuweisungsoperator angeben, und Sie müssen das Array Element für Element kopieren.


Die Begründung ist ähnlich in C (C99 §6.5.16 / 2):

Ein Zuweisungsoperator soll einen veränderbaren Wert als linken Operanden haben.

Und § 6.3.2.1 / 1:

Ein modifizierbarer L-Wert ist ein L-Wert ohne Array-Typ ... [weitere Bedingungen folgen]

In C ist die Zuweisung viel einfacher als in C ++ (§ 6.5.16.1 / 2):

In der einfachen Zuweisung (=) wird der Wert des rechten Operanden in den Typ des   Zuweisungsausdruck und ersetzt den Wert, der in dem von links angegebenen Objekt gespeichert ist   Operand.

Bei der Zuweisung von Objekten vom Strukturtyp müssen der linke und der rechte Operand den gleichen Typ haben, so dass der Wert des rechten Operanden einfach in den linken Operanden kopiert wird.


24
2017-08-09 03:42



In diesem Link: http://www2.research.att.com/~bs/bs_faq2.html  Es gibt einen Abschnitt zur Array-Zuweisung:

Die zwei grundlegenden Probleme mit Arrays sind das

  • Ein Array kennt seine eigene Größe nicht
  • Der Name eines Arrays wird bei der geringsten Provokation in einen Zeiger auf sein erstes Element umgewandelt

Und ich denke, das ist der grundlegende Unterschied zwischen Arrays und Strukturen. Eine Array-Variable ist ein Low-Level-Datenelement mit begrenzter Eigenkenntnis. Grundsätzlich ist es ein Stück Speicher und eine Möglichkeit, sich darauf einzulassen.

Daher kann der Compiler den Unterschied zwischen int a [10] und int b [20] nicht unterscheiden.

Strukturen haben jedoch nicht die gleiche Mehrdeutigkeit.


2
2017-08-09 03:40



Ich weiß, dass alle, die sich geantwortet haben, Experten in C / C ++ sind. Aber ich dachte, das ist der Hauptgrund.

num2 = num1;

Hier versuchen Sie, die Basisadresse des Arrays zu ändern, was nicht zulässig ist.

und natürlich, struct2 = Struktur1;

Hier wird Objekt struct1 einem anderen Objekt zugewiesen.


0
2017-08-09 12:29