Dies ist eine ziemlich alte Frage, aber ich werde meine 2 Cent eingeben, da es viele Antworten gibt, aber keine, die alle möglichen Methoden klar und präzise zeigt (nicht sicher über das prägnante Bit, da dies eine bekam etwas außer Kontrolle geraten. TL; DR 😉).
Ich gehe davon aus, dass das OP das übergebene Array ohne Kopieren zurückgeben wollte, um es direkt an den Aufrufer zu übergeben und an eine andere Funktion zu übergeben, damit der Code schöner aussieht.
Wenn Sie ein Array wie dieses verwenden, wird es jedoch in einen Zeiger zerfallen und vom Compiler wie ein Array behandelt. Dies kann zu subtilen Fehlern führen, wenn Sie ein Array wie übergeben, wobei die Funktion erwartet, dass es 5 Elemente enthält, Ihr Anrufer jedoch tatsächlich eine andere Nummer übergibt.
Es gibt einige Möglichkeiten, wie Sie besser damit umgehen können. Übergeben Sie ein std::vector
oder std::array
(nicht sicher, ob std::array
es 2010 war, als die Frage gestellt wurde). Sie können das Objekt dann als Referenz übergeben, ohne das Objekt kopieren / verschieben zu müssen.
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;
}
Wenn Sie jedoch darauf bestehen, mit C-Arrays zu spielen, verwenden Sie eine Vorlage, in der die Informationen über die Anzahl der Elemente im Array gespeichert sind.
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;
}
Außer, das sieht ziemlich hässlich aus und ist super schwer zu lesen. Ich benutze jetzt etwas, um mit dem zu helfen, was es 2010 nicht gab, was ich auch für Funktionszeiger verwende:
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;
}
Dies bewegt den Typ dahin, wo man ihn erwarten würde, und macht ihn weitaus lesbarer. Die Verwendung einer Vorlage ist natürlich überflüssig, wenn Sie nur 5 Elemente verwenden möchten. Sie können sie also natürlich hart codieren:
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;
}
Wie gesagt, mein type_t<>
Trick hätte zu dem Zeitpunkt, als diese Frage gestellt wurde, nicht funktioniert. Das Beste, was Sie sich damals erhofft hatten, war die Verwendung eines Typs in einer Struktur:
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;
}
Was wieder ziemlich hässlich aussieht, aber zumindest noch besser lesbar ist, obwohl das typename
damals je nach Compiler optional gewesen sein könnte, was zu Folgendem führte:
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;
}
Und dann hätten Sie natürlich einen bestimmten Typ angeben können, anstatt meinen Helfer zu verwenden.
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;
}
Damals die freien Funktionen std::begin()
und std::end()
nicht existierte, obwohl sein könnten leicht umgesetzt werden . Dies hätte eine sicherere Iteration über das Array ermöglicht, da sie auf einem C-Array sinnvoll sind, jedoch nicht auf einem Zeiger.
Für den Zugriff auf das Array können Sie es entweder an eine andere Funktion übergeben, die denselben Parametertyp verwendet, oder einen Alias erstellen (was nicht viel Sinn macht, da Sie bereits das Original in diesem Bereich haben). Der Zugriff auf eine Array-Referenz entspricht dem Zugriff auf das ursprüngliche Array.
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
oder
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
}
Zusammenfassend ist es am besten, einen Array-Zerfall in einen Zeiger nicht zuzulassen, wenn Sie beabsichtigen, darüber zu iterieren. Es ist nur eine schlechte Idee, da es den Compiler davon abhält, Sie davor zu schützen, sich in den Fuß zu schießen, und Ihren Code schwerer lesbar macht. Versuchen Sie immer, dem Compiler zu helfen, indem Sie die Typen so lange wie möglich beibehalten, es sei denn, Sie haben einen sehr guten Grund, dies nicht zu tun.
Bearbeiten
Oh, und der Vollständigkeit halber können Sie zulassen, dass es sich zu einem Zeiger verschlechtert, aber dies entkoppelt das Array von der Anzahl der darin enthaltenen Elemente. Dies geschieht häufig in C / C ++ und wird normalerweise durch Übergeben der Anzahl der Elemente im Array verringert. Der Compiler kann Ihnen jedoch nicht helfen, wenn Sie einen Fehler machen und den falschen Wert an die Anzahl der Elemente übergeben.
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
Anstatt die Größe zu übergeben, können Sie den Endzeiger übergeben, der auf einen nach dem Ende Ihres Arrays zeigt. Dies ist nützlich, da es sich um etwas handelt, das näher an den Standardalgorithmen liegt, die einen Anfangs- und einen Endzeiger verwenden. Was Sie jedoch zurückgeben, ist jetzt nur etwas, an das Sie sich erinnern müssen.
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
Alternativ können Sie dokumentieren, dass diese Funktion nur 5 Elemente benötigt, und hoffen, dass der Benutzer Ihrer Funktion nichts Dummes tut.
// 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;
}
Beachten Sie, dass der Rückgabewert seinen ursprünglichen Typ verloren hat und zu einem Zeiger herabgesetzt wird. Aus diesem Grund sind Sie jetzt allein, um sicherzustellen, dass Sie das Array nicht überlaufen.
Sie könnten ein übergeben std::pair<int*, int*>
, das Sie für Anfang und Ende verwenden und das weitergeben können, aber dann sieht es wirklich nicht mehr wie ein Array aus.
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];
}
oder
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])));
}
Komischerweise ist dies der std::initializer_list
Arbeitsweise (c ++ 11) sehr ähnlich , aber sie funktionieren in diesem Kontext nicht.