49 votes

Pourquoi C ++ ne prend-il pas en charge les fonctions renvoyant des tableaux?

Certains langages vous permettent de simplement déclarer une fonction renvoyant un tableau comme une fonction normale, comme Java:

 public String[] funcarray() {
   String[] test = new String[]{"hi", "hello"};
   return test;
}
 

Pourquoi le C ++ ne prend-il pas en charge quelque chose comme int[] funcarray(){} ? Vous pouvez renvoyer un tableau, mais c'est vraiment compliqué de créer une telle fonction. Et aussi, j'ai entendu quelque part que les cordes ne sont que des tableaux de caractères. Donc, si vous pouvez renvoyer une chaîne en C ++, pourquoi pas un tableau?

66voto

Doug Stephen Points 3913

J'avais un pari suppose que, pour être concis, c'était simplement une décision de conception. Plus précisément, si vous voulez vraiment savoir pourquoi, vous devez travailler à partir de la base.

Pensons à C en premier. Dans le langage C, il existe une distinction claire entre le "passage par référence" et de "passage par valeur". Pour les traiter à la légère, le nom d'un tableau en C est vraiment juste un pointeur. Pour toutes fins utiles, la différence (en général), revient à l'allocation. Le code

int array[n];

serait de créer 4*n octets de mémoire (sur un système 32 bits) sur la pile de corrélation à la portée de n'importe quel bloc de code fait la déclaration. À son tour,

int* array = (int*) malloc(sizeof(int)*n);

serait de créer la même quantité de mémoire, mais sur le tas. Dans ce cas, qu'est-ce que dans cette mémoire n'est pas attaché à la portée, seule la référence À la mémoire est limitée par la portée. C'est ici que le passage par valeur et le passage par référence venir. Passage par valeur, comme vous le savez probablement, cela signifie que lorsque quelque chose est passé ou renvoyée par une fonction, la "chose" qui est transmis est le résultat de l'évaluation de la variable. En d'autres termes,

int n = 4;
printf("%d", n);

imprime le numéro 4, car la construction n évalue à 4 (désolé si c'est élémentaire, je tiens juste à couvrir toutes les bases). Ce 4 n'a absolument aucune incidence ou de la relation à l'espace mémoire de votre programme, c'est juste un littéral, et donc, une fois que vous quittez la portée de qui que 4 a contexte, vous le perdez. Ce sujet passage par référence? Le passage par référence n'est pas différent dans le contexte d'une fonction; il vous suffit d'évaluer la construction qui est transmis. La seule différence est que, après l'évaluation de la "chose", vous utilisez le résultat de l'évaluation comme une adresse mémoire. Une fois, j'ai eu un particulier cynique CS instructeur qui aimait qu'il n'y a pas une telle chose comme le passage par référence, juste un moyen de passer intelligent valeurs. Vraiment, il est juste. Alors maintenant, nous pensons au sujet de la portée en termes d'une fonction. Faire semblant que vous pouvez avoir un tableau de type de retour:

int[] foo(args){
    result[n];
    // Some code
    return result;
}

Le problème ici est que le résultat donne l'adresse de l'0e élément du tableau. Mais lorsque vous essayez d'accéder à cette mémoire de l'extérieur de cette fonction (par l'intermédiaire de la valeur de retour), vous avez un problème parce que vous tentez d'accéder à la mémoire qui n'est pas dans le champ d'application avec laquelle vous travaillez (l'appel de la fonction de la pile). Donc, la façon dont nous obtenons, c'est avec la norme "passage par référence" jiggery-pokery:

int* foo(args){
    int* result = (int*) malloc(sizeof(int)*n));
    // Some code
    return result;
}

Nous obtenons toujours une adresse de mémoire pointant vers le 0e élément du Tableau, mais maintenant nous avons accès à cette mémoire.

Quel est mon point? En Java, il est commun d'affirmer que "tout est passage par valeur". Ce qui est vrai. La même cynique instructeur à partir de ci-dessus a également eu ceci à dire au sujet de Java et de la programmation orientée objet en général: Tout ce qui est un pointeur. Et il est aussi en droit. Tout en tout en Java est en fait le passage par valeur, presque toutes ces valeurs sont en fait des adresses de mémoire. Donc, en Java, le langage permet de retourner un tableau ou une Chaîne de caractères, mais il le fait en tournant dans la version avec des pointeurs pour vous. Il gère également votre mémoire pour vous. Et gestion automatique de la mémoire, bien qu'utile, n'est pas efficace.

Cela nous amène à C++. La raison pour laquelle l'ensemble C++ a été inventé était parce que Bjarne Stroustrup avait fait des expériences avec Simula (essentiellement l'original OOPL) au cours de son travail de thèse, et pensait que c'était fantastique sur le plan conceptuel, mais il a remarqué qu'il a effectué, plutôt terriblement. Et donc, il a commencé à travailler sur ce qui a été appelé C avec des Classes, qui a été renommé en C++. Ce faisant, son objectif était de faire un langage de programmation qui a eu les meilleures fonctionnalités de Simula, mais est restée puissante et rapide. Il a choisi d'étendre C en raison de sa légendaire de la performance, et un compromis a été qu'il a choisi de ne pas implémenter la gestion automatique de la mémoire ou de déchets de la collecte d'une telle ampleur, comme d'autres OOPL de l'. Retourner un tableau à partir de l'une des classes de modèle fonctionne parce que, eh bien, vous êtes à l'aide d'une classe. Mais si vous voulez retourner un C tableau, vous avez à faire c'est de la C moyen. En d'autres termes, C++ prend en charge le retour d'un tableau EXACTEMENT de la même manière que Java n'; il n'a tout simplement pas le faire tout le travail pour vous. Parce qu'un danois mec pensais que ça serait trop lent.

31voto

CashCow Points 18388

C++ prend en charge elle - même de tri de:

vector< string> func()
{
   vector<string> res;
   res.push_back( "hello" );
   res.push_back( "world" );
   return res;
}

Même C tri de la le prend en charge:

struct somearray
{
  struct somestruct d[50];
};

struct somearray func()
{
   struct somearray res;
   for( int i = 0; i < 50; ++i )
   {
      res.d[i] = whatever;
   }
   // fill them all in
   return res;
}

Un std::string est une classe, mais quand vous dites une chaîne tu veux sans doute dire un littéral. Vous pouvez retourner un littéral en toute sécurité à partir d'une fonction, mais en fait on pourrait statiquement créer un tableau et de le retourner à partir d'une fonction. Ce serait thread-safe si c'était un const (en lecture seule) de tableau qui est le cas avec les littéraux de chaîne.

La matrice de retour de se dégrader à un pointeur, donc vous ne seriez pas en mesure de travailler sur sa taille seulement de son retour.

Renvoyer un tableau, si c'était possible, il faudrait être de longueur fixe en premier lieu, étant donné que le compilateur a besoin de créer de la pile d'appel, puis a la question que les tableaux ne sont pas l-valeurs afin de les recevoir dans l'appel de la fonction serait d'utiliser une nouvelle variable avec l'initialisation, ce qui est peu pratique. Le retour de l'un peut ne pas être pratique aussi pour la même raison, atlhough ils pourraient avoir utilisé une notation spéciale pour les valeurs de retour.

Rappelez-vous dans les premiers jours de C toutes les variables ont dû être déclarée en haut de la fonction, et vous ne pouvez pas il suffit de déclarer lors de la première utilisation. Ainsi, il était impossible à l'époque.

Ils ont donné la solution de mettre le tableau dans une structure (struct) et c'est juste la façon dont il a maintenant de rester en C++, car il utilise la même convention d'appel.

Remarque: Dans des langages comme Java, un tableau est une classe. Vous en créer une nouvelle. Vous pouvez réaffecter (ils sont la l-valeurs).

26voto

Les tableaux en C et en C++ pour la compatibilité ascendante) ont spécial sémantique qui diffèrent du reste des types. En particulier, alors que pour le reste de la des types, C seulement a passer-par-valeur sémantique, dans le cas des tableaux de l'effet du passage par valeur, la syntaxe simule par référence à une étrange manière:

Dans une signature de fonction, un argument de type tableau de N éléments de type T est converti en pointeur de T. Dans un appel de fonction passage d'un tableau en argument d'une fonction de décroissance de la matrice à un pointeur vers le premier élément, et le pointeur est copié dans la fonction.

En raison de ce traitement particulier pour les tableaux --ils ne peuvent pas être passés par valeur, ils ne peuvent pas être retournés en valeur. En C, vous pouvez retourner un pointeur, et en C++, vous pouvez également retourner une référence, mais le tableau lui-même ne peut pas être alloué dans la pile.

Si vous pensez à elle, ce n'est pas différente de la langue que vous utilisez dans la question, comme le tableau est alloué dynamiquement et vous êtes seulement en retournant un pointeur/référence.

Le langage C++, d'autre part, permet différentes solutions à ce problème particulier, comme l'utilisation d' std::vector dans la norme actuelle (contenu sont alloués dynamiquement) ou std::array dans la norme à venir (le contenu peut être alloué dans la pile, mais il pourrait avoir un coût plus élevé, comme chaque élément devra être copié dans le cas où la copie ne peut pas être gommés par le compilateur). En fait, vous pouvez utiliser le même type d'approche avec la norme actuelle en utilisant off-the-shelf bibliothèques, comme boost::array.

8voto

Orbit Points 6928

"Vous ne pouvez pas retourner tableau à partir de la fonction parce que la matrice serait de déclarée à l'intérieur de la fonction, et sa emplacement serait alors la pile cadre. Cependant, la trame de pile est effacé lorsque la fonction se termine. Les fonctions doivent copie de la valeur de retour à partir de la trame de pile à lieu de retour, et ce n'est pas possible avec les tableaux."

À partir d'une discussion ici:

http://forum.codecall.net/c-c/32457-function-return-array-c.html

7voto

AProgrammer Points 31212

D'autres ont dit qu'en C++, on utilise vector<> au lieu des tableaux hérités de C.

Alors pourquoi le C++ ne permet pas de retours C des tableaux? Parce que le C ne l'est pas.

Pourquoi C ne l'est pas? Parce que C évolué à partir de B, a non typé langue dans laquelle renvoyer un tableau n'a pas de sens du tout. Lors de l'ajout de types de B, il aurait été significatif pour le rendre possible de retourner un tableau mais qui n'a pas été fait dans le but de garder certains idiomes valide et faciliter la conversion de programmes de B à C. Et depuis lors, la possibilité de faire C des tableaux plus utilisable comme toujours été refusé (et même plus, même pas considérés) comme elle allait se briser trop de code existant.

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