Comment faites-vous le sens de l'erreur: ne peut pas convertir « int [] » à « int [] »
-
22-08-2019 - |
Question
Lors de la compilation du code suivant:
void DoSomething(int Numbers[])
{
int SomeArray[] = Numbers;
}
le compilateur VS2005 se plaint avec le C2440 d'erreur: 'initialisation': ne peut pas convertir 'int []' à 'int []
Je comprends que vraiment essaie de jeter un pointeur sur un tableau qui ne va pas travailler. Mais comment expliquez-vous l'erreur à quelqu'un d'apprentissage C ++?
La solution
Il y a trois choses que vous devez expliquer à la personne que vous essayez d'aider:
-
Les tableaux ne peuvent pas être passés par valeur à une fonction en C ++. Pour faire ce que vous essayez de faire, vous devez passer l'adresse du début du tableau à
DoSomething()
, ainsi que la taille du tableau dans unint
séparé (bien,size_t
, mais je ne contrarient pas dire que) argument. Vous pouvez obtenir l'adresse du début de certainsmyArray
de tableau avec l'expression&(myArray[0])
. Comme il est une chose commune à vouloir faire, C ++ vous permet d'utiliser simplement le nom du tableau - par exemplemyArray
- pour obtenir l'adresse de son premier élément. (Ce qui peut être utile ou prêtant à confusion, selon la façon dont vous regardez.) Pour rendre les choses encore plus confuses, C ++ vous permet de spécifier un type de tableau (par exempleint Numbers[]
) en tant que paramètre à une fonction, mais en secret il traite ce paramètre comme si elle était déclarée comme un pointeur (int *Numbers
dans ce cas) - vous pouvez même faireNumbers += 5
l'intérieurDoSomething()
pour le faire pointer vers un tableau à partir de la sixième position -
Lorsque vous déclarer une variable de tableau tel que
SomeArray
en C ++, il faut soit prévoir une taille explicite ou une « liste d'initialiser » , qui est une liste séparée par des virgules des valeurs entre accolades . Il est impossible pour le compilateur de déduire la taille du tableau basé sur un autre tableau que vous essayez de lui avec l'initialisation, parce que ... -
Vous ne pouvez pas copier un tableau dans un autre, ou d'initialiser une matrice d'un autre en C ++. Donc, même si le paramètre
Numbers
était vraiment un tableau (par exemple de la taille 1000) et non un pointeur, et que vous avez spécifié la taille deSomeArray
(encore une fois comme dire 1000), laint SomeArray[1000] = Numbers;
ligne serait illégale.
Pour faire ce que vous voulez faire dans DoSomething()
, demandez-vous:
- Dois-je modifier les valeurs
Numbers
? - Si oui, ce que je veux empêcher l'appelant de voir ces changements?
Si la réponse à ces deux questions est « Non », vous ne pas en fait besoin de faire une copie de Numbers
en premier lieu -. Il suffit d'utiliser tel quel, et d'oublier de faire un tableau de SomeArray
séparé
Si la réponse aux deux questions est « oui », vous devez faire une copie de Numbers
dans SomeArray
et travailler sur ce lieu. Dans ce cas, vous devriez vraiment faire une SomeArray
C ++ vector<int>
au lieu d'un autre tableau, car cela simplifie vraiment les choses. (Expliquer les avantages des vecteurs sur l'allocation dynamique de mémoire manuelle, y compris les faits qu'ils peut procéder à l'initialisation à partir d'autres tableaux ou vecteurs, et ils appelleront les constructeurs d'éléments si nécessaire, contrairement à un memcpy()
style C.)
Autres conseils
Dire qu'il ya des types et des types incomplets:
struct A;
est un type d'une struct dite incomplète A. Alors que
struct A { };
est un type complet d'un struct appelé A. La taille de la première n'est pas encore connu, alors que la taille de la seconde est connue.
Il existe plusieurs types de classes incomplètes comme le struct ci-dessus. Mais il y a aussi des types de tableaux incomplets:
typedef int A[];
C'est un type tableau incomplet appelé A. Sa taille est pas encore connue. Vous ne pouvez pas créer un tableau hors de celui-ci, parce que le compilateur ne sait pas la taille du tableau est. Mais vous pouvez utiliser pour créer un tableau, uniquement si vous lsinitialisez immédiatement:
A SomeArray = { 1, 2, 3 };
Maintenant, le compilateur connaît le tableau est un tableau int avec 3 éléments. Si vous essayez d'initialiser le tableau avec un pointeur, le compilateur ne sera pas plus intelligent que jamais, et de refuser, parce que cela ne lui donner la taille du tableau à créer.
En essayant de rendre le message d'erreur plus utile, le compilateur est en fait les choses confuses. Même si le paramètre Numbers
est déclaré comme un tableau, C / C ++ ne pas (peut) passer en réalité un tableau -. Le paramètre Numbers
est en fait un pointeur
Alors l'erreur devrait vraiment dire "cannot convert from 'int *' to 'int []'"
Mais il y aurait confusion - "hey, il n'y a pas int*
impliqué dans l'expression", quelqu'un pourrait dire
Pour cette raison, il est vraiment préférable d'éviter les paramètres de tableau - les déclarer en tant que pointeurs, puisque c'est ce que vous êtes vraiment obtenir de toute façon. Et l'explication à quelqu'un d'apprentissage C / C ++ devrait les éduquer sur le fait que les paramètres du tableau sont une fiction -. Ils sont vraiment pointeurs
Quand je suis en train d'expliquer quelque chose, j'essaie toujours de descendre au niveau le plus bas, et de construire à partir de là. Ce qu'ils aiment est je façon d'apprendre des choses, et je trouve que les gens sont plus à l'aise si vous commencez avec les bases qu'ils connaissent, et de construire à partir de là.
Dans ce cas, je serais probablement commencer par quelque chose comme:
Le compilateur essaie de faire la mission, parce que vous avez écrit un opération d'affectation. En C ++, vous ne peut pas attribuer directement à un tableau, parce qu'il n'a pas d'affectation intégrée opérateur (de toute nature, seulement initialisation et d'indexation est supporté pour les tableaux). Parce que les supports C + opérateurs surchargées pour les types, les compilateur recherche alors une surcharge opérateur d'affectation pour la « Attribué à » type qui prend la comme argument « attribué par de » type. Comme il n'y a pas non plus surchargées opérateur int [] qui prend int [] comme argument, les erreurs du compilateur sur la ligne, et les erreurs vous dit pourquoi le compilateur ne peut pas traiter la ligne.
Oui, il est probablement exagéré vs juste dire quelque chose sur la connaissance de la taille, les types incomplets, etc. Je me rends compte qu'il est pas non plus complète (par exemple: aucune discussion de cession initialiseur vs affectation normale, etc.). Cependant, mon objectif est généralement d'amener les gens là où ils peuvent trouver la prochaine réponse se, et que vous voulez généralement exposer le processus de réflexion pour arriver à la réponse.
Peut-être votre réponse pourrait être « Parce que le compilateur ne sait pas la taille du tableau est. »
Votre exemple pourrait fonctionner s'il y avait la taille des tableaux explicites (peut-être avec un typedef pour plus de clarté), et ensuite vous pouvez expliquer pointeurs tout en introduisant des allocations de taille variable.