C'est une question assez ancienne, mais je vais mettre mes 2 cents car il y a beaucoup de réponses, mais aucune ne montre toutes les méthodes possibles de manière claire et concise (je ne suis pas sûr du bit concis, car cela a eu un peu de main. TL; DR 😉).
Je suppose que l'OP voulait retourner le tableau qui a été transmis sans copier comme moyen de le transmettre directement à l'appelant pour le passer à une autre fonction pour rendre le code plus joli.
Cependant, utiliser un tableau comme celui-ci, c'est le laisser se désintégrer en un pointeur et demander au compilateur de le traiter comme un tableau. Cela peut entraîner des bogues subtils si vous passez dans un tableau similaire, la fonction s'attendant à ce qu'il contienne 5 éléments, mais votre appelant passe en fait un autre nombre.
Il existe plusieurs façons de mieux gérer cela. Passez un std::vector
ou std::array
(je ne sais pas si std::array
c'était en 2010 lorsque la question a été posée). Vous pouvez ensuite passer l'objet comme référence sans copier / déplacer l'objet.
std::array<int, 5>& fillarr(std::array<int, 5>& arr)
{
// (before c++11)
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
// Note the following are for c++11 and higher. They will work for all
// the other examples below except for the stuff after the Edit.
// (c++11 and up)
for(auto it = std::begin(arr); it != std::end(arr); ++it)
{ /* do stuff */ }
// range for loop (c++11 and up)
for(auto& element : arr)
{ /* do stuff */ }
return arr;
}
std::vector<int>& fillarr(std::vector<int>& arr)
{
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
return arr;
}
Cependant, si vous insistez pour jouer avec les tableaux C, utilisez un modèle qui conservera les informations sur le nombre d'éléments dans le tableau.
template <size_t N>
int(&fillarr(int(&arr)[N]))[N]
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
Sauf que ça a l'air moche et super difficile à lire. J'utilise maintenant quelque chose pour aider avec ce qui n'existait pas en 2010, que j'utilise également pour les pointeurs de fonction:
template <typename T>
using type_t = T;
template <size_t N>
type_t<int(&)[N]> fillarr(type_t<int(&)[N]> arr)
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
Cela déplace le type là où on pourrait s'y attendre, ce qui le rend beaucoup plus lisible. Bien sûr, l'utilisation d'un modèle est superflue si vous n'utilisez que 5 éléments, vous pouvez donc bien sûr le coder en dur:
type_t<int(&)[5]> fillarr(type_t<int(&)[5]> arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
Comme je l'ai dit, mon type_t<>
truc n'aurait pas fonctionné au moment où cette question a été posée. Le mieux que vous auriez pu espérer à l'époque était d'utiliser un type dans une structure:
template<typename T>
struct type
{
typedef T type;
};
typename type<int(&)[5]>::type fillarr(typename type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
Ce qui recommence à être assez moche, mais au moins est encore plus lisible, bien que cela typename
puisse être facultatif à l'époque en fonction du compilateur, ce qui se traduit par:
type<int(&)[5]>::type fillarr(type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
Et puis bien sûr, vous auriez pu spécifier un type spécifique, plutôt que d'utiliser mon aide.
typedef int(&array5)[5];
array5 fillarr(array5 arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
À l' époque, les fonctions libres std::begin()
et std::end()
n'existait pas, mais auraient pu être facilement mis en œuvre. Cela aurait permis d'itérer sur le tableau d'une manière plus sûre car ils ont du sens sur un tableau C, mais pas un pointeur.
En ce qui concerne l'accès au tableau, vous pouvez soit le passer à une autre fonction qui prend le même type de paramètre, soit lui créer un alias (ce qui n'aurait pas beaucoup de sens car vous avez déjà l'original dans cette étendue). Accéder à une référence de tableau revient à accéder au tableau d'origine.
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
ou
void fn()
{
int array[5];
auto& array2 = fillarr(array); // alias. But why bother.
int forth_entry = array[4];
int forth_entry2 = array2[4]; // same value as forth_entry
}
Pour résumer, il est préférable de ne pas autoriser une désintégration de tableau dans un pointeur si vous avez l'intention de le parcourir. C'est juste une mauvaise idée car cela empêche le compilateur de vous protéger de vous tirer dans le pied et rend votre code plus difficile à lire. Essayez toujours d'aider le compilateur à vous aider en conservant les types aussi longtemps que possible, sauf si vous avez une très bonne raison de ne pas le faire.
Éditer
Oh, et pour être complet, vous pouvez lui permettre de se dégrader en un pointeur, mais cela dissocie le tableau du nombre d'éléments qu'il contient. Cela se fait beaucoup en C / C ++ et est généralement atténué en passant le nombre d'éléments dans le tableau. Cependant, le compilateur ne peut pas vous aider si vous faites une erreur et passez la mauvaise valeur au nombre d'éléments.
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
Au lieu de passer la taille, vous pouvez passer le pointeur de fin, qui pointera vers un après la fin de votre tableau. Ceci est utile car il permet d'obtenir quelque chose qui est plus proche des algorithmes std, qui prennent un pointeur de début et de fin, mais ce que vous retournez n'est maintenant que quelque chose dont vous devez vous souvenir.
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
Alternativement, vous pouvez documenter que cette fonction ne prendra que 5 éléments et espérer que l'utilisateur de votre fonction ne fera rien de stupide.
// I document that this function will ONLY take 5 elements and
// return the same array of 5 elements. If you pass in anything
// else, may nazal demons exit thine nose!
int* fillarr(int* arr)
{
for(int* it = arr; it != arr + 5; ++it)
{ /* do stuff */ }
return arr;
}
Notez que la valeur de retour a perdu son type d'origine et est dégradée en un pointeur. Pour cette raison, vous êtes maintenant seul pour vous assurer que vous n'allez pas dépasser la baie.
Vous pouvez passer un std::pair<int*, int*>
, que vous pouvez utiliser pour commencer et terminer et le faire circuler, mais cela cesse vraiment de ressembler à un tableau.
std::pair<int*, int*> fillarr(std::pair<int*, int*> arr)
{
for(int* it = arr.first; it != arr.second; ++it)
{ /* do stuff */ }
return arr; // if you change arr, then return the original arr value.
}
void fn()
{
int array[5];
auto array2 = fillarr(std::make_pair(&array[0], &array[5]));
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
ou
void other_function(std::pair<int*, int*> array)
{
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
void fn()
{
int array[5];
other_function(fillarr(std::make_pair(&array[0], &array[5])));
}
Assez drôle, c'est très similaire à la façon dont std::initializer_list
fonctionnent (c ++ 11), mais ils ne fonctionnent pas dans ce contexte.