Pourquoi les fonctions de support C ++ ne renvoient-elles pas les tableaux?

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

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

Pourquoi C ++ ne supporte-t-il pas quelque chose comme int[] funcarray(){} ? Vous pouvez retourner un tableau, mais c’est très compliqué de faire une telle fonction. Et aussi, j’ai entendu quelque part que les chaînes ne sont que des tableaux de caractères. Donc, si vous pouvez retourner une chaîne en C ++, pourquoi pas un tableau?

Je pensais 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 zéro.

Pensons à C d’abord. Dans le langage C, il existe une distinction claire entre “passer par référence” et “passer par valeur”. Pour le traiter à la légère, le nom d’un tableau dans C n’est en réalité qu’un pointeur. À toutes fins utiles, la différence (généralement) se résume à la répartition. Le code

 int array[n]; 

créerait 4 * n octets de mémoire (sur un système 32 bits) sur la stack en corrélation avec la scope du bloc de code qui fait la déclaration. À son tour,

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

créerait la même quantité de mémoire, mais sur le tas. Dans ce cas, ce qui est dans cette mémoire n’est pas lié à la scope, seule la référence à la mémoire est limitée par la scope. Voici où passer par valeur et passer par référence entrent. Passer par valeur, comme vous le savez probablement, signifie que lorsque quelque chose est passé ou renvoyé par une fonction, la “chose” qui est passée est le résultat de l’évaluation de la variable. En d’autres termes,

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

imprimera le chiffre 4 car la construction n évaluée à 4 (désolé si c’est élémentaire, je veux juste couvrir toutes les bases). Ce 4 n’a absolument aucune relation ou relation avec l’espace mémoire de votre programme, c’est juste un littéral, et une fois que vous quittez le champ d’application de ce contexte, vous le perdez. Qu’en est-il de passer par référence? Passer par référence n’est pas différent dans le contexte d’une fonction; vous évaluez simplement la construction qui est passée. La seule différence est qu’après avoir évalué la “chose” passée, vous utilisez le résultat de l’évaluation en tant qu’adresse mémoire. J’ai eu un jour un instructeur cynique CS qui aimait dire qu’il n’y avait pas de passer par référence, juste un moyen de transmettre des valeurs intelligentes. Vraiment, il a raison. Alors maintenant, nous pensons à la scope en termes de fonction. Imaginez que vous pouvez avoir un type de retour de tableau:

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

Le problème ici est que le résultat est évalué à l’adresse du 0ème élément du tableau. Mais lorsque vous tentez d’accéder à cette mémoire en dehors de cette fonction (via la valeur de retour), vous rencontrez un problème car vous tentez d’accéder à la mémoire qui n’est pas dans la scope de votre travail (la stack de l’appel de fonction). Donc, la façon dont nous contournons cela est avec le jiggery-pokery standard “pass by reference”:

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

Nous obtenons toujours une adresse mémoire pointant vers le 0ème élément du tableau, mais nous avons maintenant access à cette mémoire.

Quel est mon point? En Java, il est courant d’affirmer que “tout est passe par valeur”. C’est vrai. Le même instructeur cynique ci-dessus disait aussi à propos de Java et de la POO en général: tout n’est qu’un pointeur. Et il a aussi raison. Bien que tout en Java soit en fait passé par valeur, presque toutes ces valeurs sont en fait des adresses mémoire. Donc, en Java, le langage vous permet de renvoyer un tableau ou une chaîne, mais en le transformant en version avec des pointeurs pour vous. Il gère également votre mémoire pour vous. Et la gestion automatique de la mémoire, bien qu’utile, n’est pas efficace.

Cela nous amène à C ++. La raison pour laquelle C ++ a été inventée était que Bjarne Stroustrup avait expérimenté Simula (essentiellement l’OOPL d’origine) au cours de son travail de doctorat, et qu’il était fantastique sur le plan conceptuel, mais il a remarqué que cela fonctionnait très bien. Et donc, il a commencé à travailler sur ce qu’on appelait C avec Classes, qui a été renommé en C ++. Ce faisant, son objective était de créer un langage de programmation prenant certaines des meilleures fonctionnalités de Simula, tout en restant puissant et rapide. Il a choisi d’étendre C en raison de ses performances déjà légendaires, et il a choisi de ne pas implémenter la gestion automatique de la mémoire ou la récupération de la mémoire à grande échelle comme les autres OOPL. Le retour d’un tableau à partir de l’une des classes de modèle fonctionne car, bien, vous utilisez une classe. Mais si vous voulez retourner un tableau C, vous devez le faire de la manière C. En d’autres termes, C ++ prend en charge le renvoi d’un tableau EXACTEMENT identique à celui de Java; cela ne fait tout simplement pas tout le travail pour vous. Parce qu’un Danois pensait que ce serait trop lent.

C ++ le supporte – en quelque sorte:

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

Même C en quelque sorte le supporte:

 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::ssortingng est une classe mais quand vous dites une chaîne, vous voulez probablement dire un littéral. Vous pouvez renvoyer un littéral en toute sécurité depuis une fonction, mais en réalité, vous pouvez créer statiquement n'importe quel tableau et le renvoyer à partir d'une fonction. Ce serait un thread-safe s'il s'agissait d'un tableau const (en lecture seule), ce qui est le cas avec les littéraux de chaîne.

Le tableau que vous retournez se dégradera en pointeur, vous ne pourrez donc pas calculer sa taille à partir de son retour.

Le retour d'un tableau, s'il était possible, devrait être de longueur fixe en premier lieu, étant donné que le compilateur doit créer la stack d'appels, puis que les tableaux ne sont pas des valeurs l pour le recevoir dans la fonction d'appel devrait utiliser une nouvelle variable avec initialisation, ce qui est peu pratique. Le retour de l'un peut être peu pratique pour la même raison, bien qu'ils aient pu utiliser une notation spéciale pour les valeurs de retour.

Rappelez-vous que dans les premiers jours de C, toutes les variables devaient être déclarées en haut de la fonction et que vous ne pouviez pas simplement déclarer lors de la première utilisation. Ainsi, il était impossible à l'époque.

Ils ont fourni la solution de contournement pour mettre le tableau dans une structure et c'est comme ça qu'il doit maintenant restr en C ++ car il utilise la même convention d'appel.

Note: Dans les langages comme Java, un tableau est une classe. Vous en créez un avec nouveau. Vous pouvez les réaffecter (ils sont des valeurs l).

Les tableaux en C (et en C ++ pour la rétrocompatibilité) ont une sémantique spéciale différente des autres types. En particulier, alors que pour les autres types, C ne dispose que d’une sémantique par valeur, dans le cas des tableaux, l’effet de la syntaxe par valeur simule de manière étrange:

Dans une signature de fonction, un argument de type tableau de N éléments de type T est converti en pointeur sur T. Dans un appel de fonction, passer un tableau en argument à une fonction amènera le tableau à un pointeur sur le premier élément et ce pointeur sera copié dans la fonction.

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

Si vous y pensez, ce n’est pas différent de la langue que vous utilisez dans la question, car le tableau est alloué dynamicment et vous ne lui retournez qu’un pointeur / référence.

Le langage C ++, quant à lui, permet différentes solutions à ce problème particulier, comme l’utilisation de std::vector dans le standard actuel (les contenus sont alloués dynamicment) ou std::array dans le prochain standard (les contenus peuvent être alloués dans la stack). , mais cela peut avoir un coût plus élevé, car chaque élément devra être copié dans les cas où la copie ne peut pas être éluée par le compilateur). En fait, vous pouvez utiliser le même type d’approche avec le standard actuel en utilisant des bibliothèques standard telles que boost::array .

“Vous ne pouvez pas renvoyer de tableau depuis la fonction car ce tableau serait déclaré à l’intérieur de la fonction et son emplacement serait alors le frame de stack. Cependant, le frame de stack est effacé à la fermeture de fonction. emplacement, et ce n’est pas possible avec des tableaux. ”

De la discussion ici:

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

D’autres ont dit qu’en C ++, on utilisait un vecteur <> au lieu des tableaux hérités de C.

Alors, pourquoi C ++ ne permet pas de retourner des tableaux C? Parce que C ne le fait pas.

Pourquoi C ne le fait pas? Parce que C a évolué à partir de B, un langage non typé dans lequel le retour d’un tableau n’a aucun sens. Lors de l’ajout de types à B, il aurait été intéressant de rendre un tableau mais cela n’a pas été fait pour garder certains idiomes B valides et faciliter la conversion des programmes de B à C. Et depuis, la possibilité de rendre les tableaux C plus utilisables comme toujours été refusé (et même plus, même pas considéré) car cela briserait trop de code existant.

Vous pouvez renvoyer un pointeur sur le tableau. Faites juste attention à libérer la mémoire plus tard.

 public std::ssortingng* funcarray() { std::ssortingng* test = new std::ssortingng[2]; test[0] = "hi"; test[1] = "hello"; return test; } // somewhere else: std::ssortingng* arr = funcarray(); std::cout << arr[0] << " MisterSir" << std::endl; delete[] arr; 

Ou vous pouvez simplement utiliser l'un des conteneurs dans l'espace de noms std, comme std :: vector.

“Pourquoi C ++ ne supporte-t-il pas quelque chose comme ça”: Parce que ça n’aurait aucun sens. Dans les langages de référence tels que JAVA ou PHP, la gestion de la mémoire repose sur la récupération de la mémoire. Les portions de mémoire qui n’ont pas de références (aucune variable dans votre programme n’y pointe plus) sont automatiquement libérées. Dans ce contexte, vous pouvez allouer de la mémoire et transmettre la référence avec précaution.

Le code C ++ sera traduit en code machine et aucun GC n’y sera défini. Ainsi, en C et C ++, il existe un fort sentiment de propriété des blocs de mémoire. Vous devez savoir si le pointeur que vous utilisez est à votre disposition à tout moment (en fait, vous devriez le libérer après utilisation), ou vous avez un pointeur sur une partie partagée de la mémoire, ce qui est un non absolu à libérer.

Dans cet environnement, vous ne gagneriez rien en créant des copies sans fin d’un tableau à chaque fois qu’il passe d’une fonction à une autre. La gestion de vos tableaux de données dans des langages de type c est une tâche beaucoup plus complexe. Il n’y a pas de solution unique et vous devez savoir quand libérer de la mémoire.

Un tableau renvoyé par une fonction serait-il toujours une copie (la vôtre à libérer) ou vous devez en faire des copies? Voulez-vous gagner en obtenant un tableau d’un pointeur sur un tableau?

Retourne un std::vector<> au lieu d’un tableau. En général, les tableaux ne fonctionnent pas bien avec C ++ et devraient généralement être évités.

En outre, le type de données ssortingng n’est pas simplement un tableau de caractères, bien qu’une «chaîne citée» soit. La ssortingng gère un tableau de caractères et vous pouvez y accéder avec .c_str() , mais la ssortingng ne se .c_str() pas à cela.

Découvrez ici Très utile.

  • Comment retourner un tableau à partir d’une fonction?
  • C ++ Retourne le tableau multidimensionnel de la fonction
  • Retourne le tableau 2d de la fonction en C ++