Je suis assez nouveau en C++ et j'évite les pointeurs. D'après ce que j'ai lu en ligne, je ne peux pas retourner un tableau mais je peux y retourner un pointeur. J'ai fait un petit code pour le tester et je me demandais si c'était la façon normale/correcte de le faire:
#include <iostream>
using namespace std;
int* test (int in[5]) {
int* out = in;
return out;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int* pArr = test(arr);
for (int i = 0; i < 5; i++) cout<<pArr[i]<<endl;
cout<<endl;
return 0;
}
Edit: Cela ne semble pas être bon. Comment dois-je le réécrire?
int* test (int a[5], int b[5]) {
int c[5];
for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
int* out = c;
return out;
}
Votre code tel qu'il est est correct mais j'ai du mal à comprendre comment il pourrait/serait utilisé dans un scénario réel. Cela dit, veuillez noter quelques mises en garde lors du retour de pointeurs à partir de fonctions:
int arr[5];
, Il est alloué sur la pile et est local à la fonction.arr
à test()
.std::unique_ptr
/std::shared_ptr<>
.Edit - pour répondre au cas d'utilisation de la multiplication matricielle
Vous avez deux options. La manière naïve est d'utiliser std::unique_ptr
/std::shared_ptr<>
. La méthode C++ moderne consiste à avoir une classe Matrix
où vous surchargez operator *
Et vous devez absolument utiliser le nouveau rvalue references
Si vous voulez éviter de copier le résultat de la multiplication pour obtenir hors de la fonction. En plus d'avoir vos copy constructor
, operator =
Et destructor
, vous devez également avoir move constructor
Et move assignment operator
. Parcourez les questions et réponses de cette recherche pour avoir plus d'informations sur la façon d'y parvenir.
Edit 2 - réponse à la question jointe
int* test (int a[5], int b[5]) {
int *c = new int[5];
for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
return c;
}
Si vous l'utilisez comme int *res = test(a,b);
, puis quelque temps plus tard dans votre code, vous devez appeler delete []res
Pour libérer la mémoire allouée dans la fonction test()
. Vous voyez maintenant que le problème est qu'il est extrêmement difficile de garder une trace manuelle du moment où faire l'appel à delete
. D'où les approches sur la façon de le gérer, telles que décrites dans la réponse.
Votre code est OK. Notez cependant que si vous renvoyez un pointeur vers un tableau et que ce tableau sort de la portée, vous ne devez plus utiliser ce pointeur. Exemple:
int* test (void)
{
int out[5];
return out;
}
Ce qui précède ne fonctionnera jamais, car out
n'existe plus lorsque test()
revient. Le pointeur renvoyé ne doit plus être utilisé. Si vous faites l'utilisez, vous lirez/écrivez dans la mémoire que vous ne devriez pas.
Dans votre code d'origine, le tableau arr
sort de la portée lorsque main()
revient. Évidemment, ce n'est pas un problème, car le retour de main()
signifie également que votre programme se termine.
Si vous voulez quelque chose qui restera et ne pourra pas sortir de la portée, vous devez l'allouer avec new
:
int* test (void)
{
int* out = new int[5];
return out;
}
Le pointeur renvoyé sera toujours valide. N'oubliez pas de le supprimer à nouveau lorsque vous en avez terminé, en utilisant delete[]
:
int* array = test();
// ...
// Done with the array.
delete[] array;
Le supprimer est le seul moyen de récupérer la mémoire qu'il utilise.
Nouvelle réponse à une nouvelle question:
Vous ne pouvez pas retourner le pointeur sur la variable automatique (int c[5]
) de la fonction. La variable automatique termine sa durée de vie avec un bloc englobant de retour (fonction dans ce cas) - vous renvoyez donc le pointeur vers un tableau non existant.
Soit dynamisez votre variable:
int* test (int a[5], int b[5]) {
int* c = new int[5];
for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
return c;
}
Ou modifiez votre implémentation pour utiliser std::array
:
std::array<int,5> test (const std::array<int,5>& a, const std::array<int,5>& b)
{
std::array<int,5> c;
for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
return c;
}
Si votre compilateur ne fournit pas std::array
vous pouvez le remplacer par une structure simple contenant un tableau:
struct array_int_5 {
int data[5];
int& operator [](int i) { return data[i]; }
int operator const [](int i) { return data[i]; }
};
Ancienne réponse à l'ancienne question:
Votre code est correct, et ... hmm, eh bien, ... inutile. Étant donné que les tableaux peuvent être attribués à des pointeurs sans fonction supplémentaire (notez que vous l'utilisez déjà dans votre fonction):
int arr[5] = {1, 2, 3, 4, 5};
//int* pArr = test(arr);
int* pArr = arr;
De plus la signature de votre fonction:
int* test (int in[5])
Est équivalent à:
int* test (int* in)
Vous voyez donc que cela n'a aucun sens.
Cependant, cette signature prend un tableau, pas un pointeur:
int* test (int (&in)[5])
Une variable référençant un tableau est fondamentalement un pointeur sur son premier élément, donc oui, vous pouvez légitimement renvoyer un pointeur sur un tableau, car ce sont essentiellement la même chose. Vérifiez vous-même:
#include <assert.h>
int main() {
int a[] = {1, 2, 3, 4, 5};
int* pArr = a;
int* pFirstElem = &(a[0]);
assert(a == pArr);
assert(a == pFirstElem);
return 0;
}
Cela signifie également que en passant un tableau à une fonction doit être fait via un pointeur (et non via int in[5]
), et éventuellement avec la longueur du tableau:
int* test(int* in, int len) {
int* out = in;
return out;
}
Cela dit, vous avez raison, l'utilisation de pointeurs (sans bien les comprendre) est assez dangereuse. Par exemple, le fait de référencer un tableau qui a été alloué sur la pile et qui est hors de portée donne un comportement non défini :
#include <iostream>
using namespace std;
int main() {
int* pArr = 0;
{
int a[] = {1, 2, 3, 4, 5};
pArr = a; // or test(a) if you wish
}
// a[] went out of scope here, but pArr holds a pointer to it
// all bets are off, this can output "1", output 1st chapter
// of "Romeo and Juliet", crash the program or destroy the
// universe
cout << pArr[0] << endl; // WRONG!
return 0;
}
Donc, si vous ne vous sentez pas suffisamment compétent, utilisez simplement std::vector
.
[réponse à la question mise à jour]
La bonne façon d'écrire votre fonction test
est soit ceci:
void test(int* a, int* b, int* c, int len) {
for (int i = 0; i < len; ++i) c[i] = a[i] + b[i];
}
...
int main() {
int a[5] = {...}, b[5] = {...}, c[5] = {};
test(a, b, c, 5);
// c now holds the result
}
Ou ceci (en utilisant std::vector
):
#include <vector>
vector<int> test(const vector<int>& a, const vector<int>& b) {
vector<int> result(a.size());
for (int i = 0; i < a.size(); ++i) {
result[i] = a[i] + b[i];
}
return result; // copy will be elided
}
Dans une application réelle, la façon dont vous avez renvoyé le tableau est appelée en utilisant un paramètre out. Bien sûr, vous n'êtes pas obligé de renvoyer un pointeur sur le tableau, car l'appelant l'a déjà, il vous suffit de remplir le tableau. Il est également courant de passer un autre argument spécifiant la taille du tableau afin de ne pas le déborder.
L'utilisation d'un paramètre de sortie présente l'inconvénient que l'appelant peut ne pas connaître la taille du tableau pour stocker le résultat. Dans ce cas, vous pouvez renvoyer une instance de classe de tableau std :: vector ou similaire.
Votre code (qui semble correct) ne renvoie pas de pointeur sur un tableau. Il renvoie un pointeur vers le premier élément de un tableau.
En fait, c'est généralement ce que vous voulez faire. La plupart des manipulations de tableaux se font via des pointeurs vers des éléments individuels, et non via des pointeurs vers le tableau dans son ensemble.
Vous pouvez définir un pointeur vers un tableau, par exemple ceci:
double (*p)[42];
définit p
comme un pointeur vers un tableau à 42 éléments de double
s. Un gros problème avec cela est que vous devez spécifier le nombre d'éléments dans le tableau dans le cadre du type - et ce nombre doit être une constante au moment de la compilation. La plupart des programmes qui traitent des tableaux doivent traiter des tableaux de tailles différentes; la taille d'un tableau donné ne variera pas après sa création, mais sa taille initiale n'est pas nécessairement connue au moment de la compilation, et différents objets du tableau peuvent avoir des tailles différentes.
Un pointeur sur le premier élément d'un tableau vous permet d'utiliser l'arithmétique du pointeur ou l'opérateur d'indexation []
pour parcourir les éléments du tableau. Mais le pointeur ne vous dit pas combien d'éléments le tableau a; vous devez généralement en faire le suivi vous-même.
Si une fonction doit créer un tableau et renvoyer un pointeur sur son premier élément, vous devez gérer vous-même le stockage de ce tableau, de plusieurs manières. Vous pouvez demander à l'appelant de passer un pointeur vers (le premier élément) d'un objet tableau, probablement avec un autre argument spécifiant sa taille - ce qui signifie que l'appelant doit savoir la taille du tableau. Ou la fonction peut renvoyer un pointeur vers (le premier élément de) un tableau statique défini à l'intérieur de la fonction - ce qui signifie que la taille du tableau est fixe, et le même tableau sera encombré par un deuxième appel à la fonction. Ou la fonction peut allouer le tableau sur le tas - ce qui rend l'appelant responsable de le désallouer plus tard.
Tout ce que j'ai écrit jusqu'à présent est commun au C et au C++, et en fait, il est beaucoup plus dans le style du C que du C++. La section 6 de la comp.lang.c FAQ traite du comportement des tableaux et des pointeurs en C.
Mais si vous écrivez en C++, vous feriez probablement mieux d'utiliser des idiomes C++. Par exemple, la bibliothèque standard C++ fournit un certain nombre d'en-têtes définissant des classes de conteneur telles que <vector>
et <array>
, qui s'occupera de la plupart de ces trucs pour vous. Sauf si vous avez une raison particulière d'utiliser des tableaux et des pointeurs bruts, il vaut probablement mieux utiliser à la place des conteneurs C++.
EDIT: Je pense que vous avez modifié votre question pendant que je tapais cette réponse. Le nouveau code à la fin de votre question n'est pas, comme vous l'observateur, bon; elle renvoie un pointeur sur un objet qui cesse d'exister dès que la fonction revient. Je pense que j'ai couvert les alternatives.
vous pouvez (en quelque sorte) retourner un tableau
au lieu de
int m1[5] = {1, 2, 3, 4, 5};
int m2[5] = {6, 7, 8, 9, 10};
int* m3 = test(m1, m2);
écrire
struct mystruct
{
int arr[5];
};
int m1[5] = {1, 2, 3, 4, 5};
int m2[5] = {6, 7, 8, 9, 10};
mystruct m3 = test(m1,m2);
où le test ressemble
struct mystruct test(int m1[5], int m2[5])
{
struct mystruct s;
for (int i = 0; i < 5; ++i ) s.arr[i]=m1[i]+m2[i];
return s;
}
pas très efficace car on copie il délivre une copie du tableau