91 votes

Pourquoi C ++ prend-il en charge l'affectation de tableaux par les membres dans les structures, mais pas en général?

Je comprends que memberwise affectation de tableaux n'est pas pris en charge, tels que le code suivant ne fonctionnera pas:

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

J'ai juste accepté ce fait, en pensant que le but de ce langage est de fournir un cadre flexible, et de permettre à l'utilisateur de décider de la façon de mettre en place quelque chose comme la copie d'un tableau.

Toutefois, le code suivant fonctionne:

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

Le tableau num[3] est membre-sage attribué de son instance en struct1, dans son instance en struct2.

Pourquoi est membre de sages-affectation de tableaux de prise en charge pour les structures, mais pas en général?

edit: Roger Pate'commentaire dans le fil std::string struct - Copier/problèmes d'affectation? semble pointer dans la direction générale de la réponse, mais je ne sais pas assez pour confirmer moi-même.

edit 2: Beaucoup d'excellentes réponses. - Je choisir Luther Blissett's parce que j'ai été pour la plupart demandais à propos de la philosophie ou de justification historique derrière le comportement, mais James McNellisde référence liées à la spec de la documentation a été utile.

50voto

Nordic Mainframe Points 13717

Voici mon prendre sur elle:

Le Développement du Langage C offre un aperçu de l'évolution du type de tableau dans C:

Je vais essayer de décrire le tableau de chose:

C précurseurs B et BCPL avait pas de distinction de type tableau, une déclaration comme:

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

déclarer V (non typé) pointeur est initialisé à point à un solde non utilisé de la région de 10 "mots" de la mémoire. B déjà utilisé * pour le déréférencement du pointeur et avait l' [] court main notation, *(V+i) signifiait V[i], tout comme en C/C++ aujourd'hui. Toutefois, V n'est pas un tableau, c'est toujours un pointeur qui a pour point de mémoire. Ce qui a causé le problème quand Dennis Ritchie a essayé d'étendre B avec struct types. Il voulait des matrices à une partie de leurs structures, comme dans C aujourd'hui:

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

Mais avec le B,BCPL concept de tableaux de pointeurs, il aurait fallu, pour l' name champ pour contenir un pointeur qui devait être initialisé lors de l'exécution d'une région de mémoire de 14 octets à l'intérieur de la structure. L'initialisation/layout problème a finalement été résolu en donnant des tableaux d'un traitement spécial: Le compilateur permettrait de suivre l'emplacement de tableaux dans les structures, sur la pile etc. sans réellement exigeant le pointeur vers les données de matérialiser, sauf dans les expressions qui impliquent les tableaux. Ce traitement a permis presque tous les B code de toujours exécuter et est la source de la "tableaux de convertir pointeur si vous regardez" la règle. C'est une mise à niveau hack, qui s'est avéré être très pratique, car il a permis à des tableaux de ouvert taille etc.

Et voici ma deviner pourquoi le tableau ne peut pas être attribué: comme les tableaux ont été pointeurs dans B, il vous suffit d'écrire:

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

pour rebase un "tableau". C'était maintenant vide de sens, parce que la base d'une variable de tableau n'était pas une lvalue plus. Donc, cette affectation a été refusé, ce qui a aidé à attraper les quelques programmes qui ne ce rebasage déclarées tableaux. Et puis cette notion coincé: Que les tableaux n'ont jamais été conçus pour être premier de la classe citized de la C type de système, ils ont été pour la plupart traités spéciaux bêtes qui deviennent pointeur si vous les utilisez. Et à partir d'un certain point de vue (qui ignore que le C-les tableaux sont bâclé hack), le refus de tableau d'affectation fait encore des sens: ouvrir Un tableau ou un tableau en paramètre de la fonction est considéré comme un pointeur sans informations sur la taille. Le compilateur n'ont pas l'information pour générer un tableau d'affectation pour eux et le pointeur de la cession a été nécessaire pour des raisons de compatibilité. L'introduction de tableau d'affectation pour l'déclaré tableaux ont introduit des bugs si fausses assignations (a=b un pointeur cession ou d'une elementwise copie?) et d'autres problèmes (comment passer un tableau de la valeur?) sans la résolution d'un problème - il suffit de faire tout ce qui explicite avec 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
}

Cela n'a pas changé lors de la révision de la C en 1978 ajoutée structure d'affectation ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Même si les enregistrements ont été de deux types distincts de C, il n'était pas possible de les attribuer au début de K&R C. Vous avez eu à copier des membres de la sagesse avec memcpy et vous pouvez passer uniquement les pointeurs comme paramètres de la fonction. Affectation (et passage de paramètres) est maintenant défini simplement comme le memcpy de la structure de la mémoire brute et depuis cela ne pouvait pas briser la gratuit de code, on a facilement adpoted. Comme un effet secondaire involontaire, ce implicitement introduit une sorte de tableau d'affectation, mais cette lancée quelque part à l'intérieur d'une structure, donc ce n'est pas vraiment de créer des problèmes avec la manière dont les tableaux ont été utilisés.

31voto

James McNellis Points 193607

Concernant les opérateurs d'affectation, la norme C++ est dit ce qui suit (C++03 §5.17/1):

Il y a plusieurs opérateurs d'affectation... tous besoin d'un modifiable lvalue comme leur opérande de gauche

Un tableau n'est pas modifiable lvalue.

Toutefois, l'affectation à une classe de type objet est défini spécialement (§5.17/4):

L'affectation à des objets d'une classe est définie par l'opérateur d'assignation de copie.

Donc, nous attendons de voir ce que la implicitement déclarées opérateur d'assignation de copie pour une classe (§12.8/13):

La implicitement défini par l'opérateur d'assignation de copie de la classe X effectue memberwise la cession de ses sous-objets. ... Chaque sous-objet est affecté de la manière appropriée pour son type:
...
-- si le sous-objet est un tableau, chaque élément est affecté, dans la manière appropriée pour le type de l'élément
...

Ainsi, pour un type de classe d'objets, les tableaux sont copiés correctement. Notez que si vous fournissez un utilisateur déclaré opérateur d'assignation de copie, vous ne pouvez pas prendre avantage de cela, et vous devrez copier le tableau élément par élément.


Le raisonnement est similaire en C (C99 §6.5.16/2):

Un opérateur d'affectation doit avoir une modifiables lvalue que son opérande de gauche.

Et §6.3.2.1/1:

Modifiable lvalue est une lvalue qui n'a pas de type de tableau de... [à d'autres contraintes suivre]

En C, l'affectation est beaucoup plus simple qu'en C++ (§6.5.16.1/2):

Dans une simple affectation (=), la valeur de l'opérande de droite est converti dans le type de la expression d'affectation et remplace la valeur stockée dans l'objet désigné par la gauche l'opérande.

Pour l'affectation de la structure-type des objets, la gauche et la droite opérandes doivent avoir le même type, la valeur de l'opérande de droite est simplement copiés dans l'opérande de gauche.

2voto

Scott Turley Points 61

Dans ce lien: http://www2.research.att.com/~bs/bs_faq2.html il y a une section sur le tableau d'affectation:

Les deux problèmes fondamentaux de tableaux sont que

  • un tableau ne connaît pas son propre taille
  • le nom d'un tableau convertit un pointeur vers son premier élément à la moindre provocation

Et je pense que c'est la différence fondamentale entre les tableaux et les structures. Une variable tableau est un faible niveau de l'élément de données avec peu de connaissance de soi. Fondamentalement, c'est une partie de la mémoire et un moyen de l'indice.

Ainsi, le compilateur ne peut pas faire la différence entre l'int a[10] et int b[20].

Les structures, cependant, n'ont pas la même ambiguïté.

0voto

user373215 Points 561

Je sais que tous ceux qui ont répondu sont des experts en C / C ++. Mais je pensais que c’était la raison principale.

num2 = num1;

Ici, vous essayez de changer l'adresse de base du tableau, ce qui n'est pas autorisé.

et bien sûr, struct2 = struct1;

Ici, l'objet struct1 est affecté à un autre objet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X