445 votes

Tableaux de longueur variable en C++ ?

Je n'ai pas utilisé le C beaucoup dans les dernières années. Quand j'ai lu cette question aujourd'hui, je suis tombé sur certains de la syntaxe C dont je n'étais pas familier avec.

Apparemment en C99 la syntaxe suivante est valide:

void foo(int n) {
    int values[n]; //Declare a variable length array
}

Cela semble être une fonctionnalité très utile, personne ne maintenant, si jamais il y avait une discussion sur l'ajoutant à la norme C++, et si oui, pourquoi il a été omis? Quelques raisons possibles:

  • Poilu pour les éditeurs de compilateurs à mettre en œuvre
  • Incompatible avec une autre partie de la norme
  • La fonctionnalité peut être émulé avec d'autres constructions C++
  • ???

Le C++ standard indique que la taille du tableau doit être une expression constante (8.3.4.1)

Oui, bien sûr, je me rends compte que dans la toy exemple on pourrait utiliser std::vector<int> values(m); mais ce alloue de la mémoire dans le tas et pas la pile. Et si je veux un tableau multidimensionnel comme:

void foo(int x, int y, int z) {
    int values[x][y][z]; //Declare a variable length array
}

l' vector version est assez maladroit

void foo(int x, int y, int z) {
    vector< vector< vector<int> > > values(/*really painful expression here*/);
}

Les tranches, les lignes et les colonnes seront également potentiellement se propager partout dans la mémoire.

EDIT: en Regardant la discussion à l' comp.std.c++ il est clair que cette question est assez controversé avec quelques très lourd noms sur les deux côtés de l'argument. Ce n'est certainement pas évident que l' std::vector est toujours une meilleure solution.

347voto

Quuxplusone Points 4320

(Contexte: j'ai une certaine expérience de la mise en œuvre de compilateurs C et C++.)

De longueur Variable des tableaux en C99 ont été essentiellement un faux-pas. Afin de soutenir Blaise, C99 eu à faire les concessions suivantes pour le sens commun:

  • sizeof x n'est plus toujours une constante de compilation, le compilateur doit parfois générer du code pour évaluer un sizeof-expression au moment de l'exécution.

  • Permettant à deux dimensions VLAs (int A[x][y]) besoin d'une nouvelle syntaxe pour la déclaration des fonctions 2D VLAs comme paramètres: void foo(int n, int A[][*]).

  • De moins en moins important dans le C++ monde, mais extrêmement important pour les C de cible du embarqués-systèmes de programmeurs, de la déclaration d'une VLA signifie mâchent un arbitrairement grande partie de votre pile. C'est une garantie de la pile de dépassement et de crash. (À tout moment, vous déclarez int A[n], vous êtes implicitement affirmer que vous avez 2 go de pile de rechange. Après tout, si vous savez "n est certainement moins de 1000 ici", puis vous devez simplement déclarer int A[1000]. La substitution de l'entier de 32 bits n pour 1000 est un aveu que vous n'avez aucune idée de ce que le comportement de votre programme devrait être.)

Ok, donc passons à parler de C++ maintenant. En C++, nous avons la même forte distinction entre "type de système" et "système de valeur" que C89 n'... mais nous avons vraiment commencé à compter sur lui dans les moyens que C n'a pas. Par exemple:

template<typename T> struct S { ... };
int A[n];
S<decltype(A)> s;  // equivalently, S<int[n]> s;

Si n n'étaient pas une constante de compilation (c'est à dire, si A étaient de variable du type modifié), alors que sur la terre serait le type d' S? Serait - Ss'type aussi être déterminé qu'au moment de l'exécution?

Ce:

template<typename T> bool myfunc(T& t1, T& t2) { ... };
int A1[n1], A2[n2];
myfunc(A1, A2);

Le compilateur doit générer du code pour certaines instanciation d' myfunc. Ce que le code devrait ressembler? Comment pouvons-nous statiquement générer ce code, si nous ne connaissons pas le type de l' A1 au moment de la compilation?

Pire, que si elle s'avère au moment de l'exécution n1 != n2, de sorte qu' !std::is_same<decltype(A1), decltype(A2)>()? Dans ce cas, l'appel à l' myfunc ne devrait même pas compiler, parce que le modèle type de déduction doit échouer! Comment pourrions-nous nous en reproduire le comportement au moment de l'exécution?

Fondamentalement, C++ est en mouvement dans le sens de pousse de plus en plus de décisions en temps de compilation: modèle de génération de code, constexpr l'évaluation de la fonction, et ainsi de suite. Pendant ce temps, C99 était occupé à pousser traditionnellement au moment de la compilation des décisions (par exemple, sizeof) dans le runtime. Avec cela à l'esprit, est-il vraiment de sens de dépenser de l'effort d'essayer d'intégrer C99-style VLAs en C++?

Comme tous les autres répondeur l'a déjà souligné, C++ fournit beaucoup de tas de mécanismes d'allocation (std::unique_ptr<int[]> A = new int[n]; ou std::vector<int> A(n); étant les plus évidents) quand vous voulez exprimer l'idée "je n'ai aucune idée de combien de RAM je pourrais en avoir besoin." Et le C++ fournit une chouette exception modèle de gestion pour faire face à l'inévitable situation que la quantité de RAM que vous avez besoin est supérieure à la quantité de RAM que vous avez. Mais j'espère que cette réponse vous donne une bonne idée de pourquoi C99-style VLAs étaient pas un bon ajustement pour le langage C++ et même pas vraiment un bon ajustement pour le C99. ;)

248voto

Johannes Schaub - litb Points 256113

Il y a récemment eu une discussion à propos de ce débuté en usenet: Pourquoi pas VLAs en C++0x.

Je suis d'accord avec ceux qui semblent être d'accord que d'avoir à créer un potentiel grand tableau sur la pile, ce qui a généralement peu d'espace disponible, n'est pas bon. L'argument est, si vous connaissez la taille à l'avance, vous pouvez aussi utiliser un tableau statique. Et si vous ne connaissez pas la taille à l'avance, vous aurez à écrire le code unsafe.

C99 VLAs pourrait donner un petit avantage de pouvoir créer de petits tableaux sans perdre de l'espace ou de l'appel de constructeurs pour ne pas les éléments utilisés, mais ils vont introduire des changements assez importants pour le système de type (vous devez être en mesure de spécifier les types en fonction des valeurs de l'exécution - ce qui n'existe pas encore au courant, C++, sauf pour new de type opérateur de spécificateurs, mais ils sont spécialement traités, de sorte que le moteur d'exécution-ness n'échappe pas à la portée de l' new opérateur).

Vous pouvez utiliser std::vector, mais il n'est pas tout à fait le même, car il utilise de la mémoire dynamique, et en rendant l'utilisation de celles propres à la pile de l'allocateur n'est pas exactement facile (alignement est un problème, trop). Il ne suffit pas à résoudre le même problème, en raison d'un vecteur est un redimensionnable conteneur, tandis que Blaise sont de taille fixe. Le C++ Tableau Dynamique proposition vise à introduire une bibliothèque de base de la solution, comme alternative à un langage basé VLA. Cependant, il ne va pas être une partie de C++0x, autant que je sache.

34voto

PfhorSlayer Points 771

Vous pouvez toujours utiliser alloca() à allouer de la mémoire sur la pile lors de l’exécution, si vous vouliez :

Étant allouée sur la pile implique qu’il sera automatiquement libérée lorsque la pile se déroule.

Note rapide : comme mentionné dans la page de manuel de Mac OS X pour alloca(3), « la fonction alloca() est machine et compilateur dépendant ; son utilisation est dis-couraged. » Juste pour vous le savez.

15voto

Il y a des situations où l'allocation de la mémoire du tas est très cher par rapport à des opérations effectuées. Un exemple est la matrice de mathématiques. Si vous travaillez avec de petits matrices dire de 5 à 10 éléments et de faire beaucoup de l'arithmétique que le malloc les frais généraux sont vraiment importantes. Dans le même temps, la taille de la compilation constante de temps ne semble pas très coûteux et inflexible.

Je pense que le C++ est donc dangereux en lui-même que l'argument "d'essayer de ne pas ajouter plus dangereux fonctionnalités" n'est pas très forte. D'autre part, comme le C++ est sans doute le plus runtime efficace langage de programmation de fonctionnalités qui le rend plus sont donc toujours utile: les Gens qui écrivent des critiques pour les performances des programmes dans une large mesure l'utilisation de C++, et ils ont besoin d'autant de la performance que possible. Le déplacement des trucs de segment de pile est une possibilité. La réduction du nombre de blocs de segment de mémoire en est une autre. Permettant à Blaise que les membres de l'objet serait un moyen d'y parvenir. Je suis en train de travailler sur une telle suggestion. C'est un peu compliqué à mettre en place, certes, mais il semble tout à fait faisable.

11voto

philsquared Points 13547

Cela a été envisagé d’inclure dans C + c++ / 1 x, mais a été abandonné (il s’agit d’une correction à ce que j’ai dit précédemment).

Il serait moins utile en C++ de toute façon puisque nous avons déjà `` pour remplir ce rôle.

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