FAQ C++ moderne et erreurs courantes
Ce ou ces sujets font partie d'une FAQ en cours d'écriture, vos retours sont les bienvenus.
Parce que si vous consultez ce sujet, vous codez en C++ et non en C !
Les tableaux C-style (tableaux déclarés sous la forme Type nom[TAILLE];) ne sont qu'un malheureux héritage du C et, à ce titre, traînent avec eux plusieurs problèmes dont le plus contraignant, la conversion implicite en pointeur.
- Lors du passage en paramètre seule l'adresse est passée, l'information sur la taille disparaît, et le contrôle sur la taille avec(1). Ceci, même si vous la mentionnez en paramètre de la fonction : elle ne reste qu'une information pour le lecteur et le compilateur ne donnera pas d'erreur si vous passez un tableau de taille différente. Vous avez dès lors obligation avec un tableau C-style de passer sa taille comme paramètre supplémentaire. Inutile de penser à l'opérateur
sizeof
: celui-ci vous renverra la taille du pointeur, pas du tableau (cf. ce sujet[Erreur courante] Utilisation de sizeof) ;
- De même le retour d'un tableau C depuis une fonction ne fait que retourner l'adresse de ce tableau qui a de bonnes chances d'être « local », et qui dit « local » dit détruit à la sortie de la fonction, et donc inutilisable passé son appel (cf. ce sujet[Erreur courante] Retourner un tableau depuis une fonction de la FAQ C) ;
- Les copies du style tab2
=
tab1; vous ont déjà laissé des surprises ? Des erreurs de compilation, des valeurs inchangées, des accès à des zones mémoire ayant été détruites auparavant, des fuites de mémoire… Non un tableau n'est pas affectable ainsi, et vers un pointeur ceci ne copie pas chaque valeur du tableau d'origine, vous copiez seulement son adresse. Et si le tableau d'origine est détruit/libéré, votre pointeur devient alors un dangling pointer ;
- Vous connaissez peut-être les VLA (Variable Length Arrays), apparus en C avec la norme C99, qui vous permettent de créer des tableaux de taille dynamique sans vous soucier de la gestion de la mémoire ? Tentants. mais 1) ils ne sont pas sans risquesTableaux VLA (Variable Length Arrays), 2) ils n'existent pas dans le standard C++(2). L'erreur de les créer est pourtant facile à commettre : il suffit d'utiliser une variable non constante comme taille. Mais, pourquoi voudriez-vous utiliser ces vilains tableaux quand vous avez std::vector à votre disposition ?
- Les tableaux C-style et pointeurs ne vous donnent aucun moyen de vérifier les accès.
Là où std::array vous propose :
- une taille transportée par son type-même et dont l'accès s'effectue tout naturellement par sa fonction membre size(), appel qui sera inliné pendant la compilation. Passage en paramètre, affectation, copie… une taille qui ne pourra jamais être erronée, et conduira à une erreur de compilation à la moindre différence ;
- un comportement semblable à tout autre objet lors du passage en paramètre d'entrée ou de retour : plus aucun risque de perdre sa taille ou rencontrer un quelconque problème lié aux pointeurs. Un passage par valeur aura le résultat attendu : une copie pure et dure de chaque valeur ;
- les mêmes services que les autres conteneurs de la bibliothèque standard (en particulier begin() / end() leur permettant d'être utilisés dans des boucles for-range en toute circonstance) ;
- services dont font également partis les différents opérateurs de comparaison lexicographiques ==/!=, d'inégalité </>/<=/>=, d'affectation = et constructeur par copie qui eux s'appliquent bien sur chaque élément du tableau au lieu de l'adresse du premier élément ;
- la possibilité d'être construit au sein d'un appel de fonction, sans avoir à utiliser de variable intermédiaire ;
- la possibilité de vérifier vos accès par le biais d'assertions en passant les bonnes options de compilation (
-
D_GLIBCXX_DEBUG pour GCC et Clang).
Vous continuez à vouloir utiliser un tableau C-style parce que…
-
vous pensez que cela reste plus performant ?
Non, il n'y a aucune différence, std::
array n'introduit aucun surcoût durant l'exécution ;
-
vous travaillez avec une bibliothèque C, et ne pensez pas avoir le choix ?
Vous l'avez, std::
array (ainsi que std::
string et std::
vector) fournissent les fonctions membre c_str()/data()(3) et size() permettant d'accéder à l'adresse et la taille du tableau qu'ils gèrent. Ils peuvent donc parfaitement être utilisés avec des fonctions telles que send().
Il existe bien la solution du passage par référence, mais elle est peu connue et utilisée.
« Mais je n'obtiens pas d'erreur » vous dites-vous ? Détrompez-vous il s'agit simplement d'une extension du compilateur que vous utilisez. Rajoutez-lui l'option de compilation -
pedantic(-
errors) et il saura vous le dire.
Avant C++17 &
arr[0
] ou &
arr.front() pour la version non constante.
Les sources présentées sur cette page sont libres de droits
et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation
constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ©
2021 Winjerome. Aucune reproduction, même partielle, ne peut être
faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc.
sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à
trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.