Bu oldukça eski bir soru, ama çok fazla cevap olduğu için 2 sentime koyacağım, ancak hiçbiri tüm olası yöntemleri net ve özlü bir şekilde göstermiyor (özlü bitden emin değilim, çünkü bu bir biraz elden. TL; DR 😉).
Ben OP doğrudan kodun daha güzel görünmesi için başka bir işleve geçirilmesi için arayan bu doğrudan geçme bazı araçlar olarak kopyalamadan geçirilen dizi dönmek istediğini varsayıyorum.
Bununla birlikte, böyle bir dizi kullanmak, bir işaretçiye bozunmasına ve derleyicinin bir dizi gibi davranmasına izin vermektir . Bunun gibi bir diziye geçerseniz işlev 5 öğeye sahip olmasını beklerken küçük hatalara neden olabilir, ancak aracınız aslında başka bir sayıdan geçer.
Bunu daha iyi ele almanın birkaç yolu var. Birstd::vector
ya dastd::array
( std::array
soru sorulduğunda 2010 yılında olup olmadığından emin değilim ). Daha sonra nesneyi kopyalama / taşıma olmadan nesneyi başvuru olarak iletebilirsiniz.
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;
}
Bununla birlikte, C dizileriyle oynamakta ısrar ediyorsanız, dizideki kaç öğenin bilgilerini tutacak bir şablon kullanın.
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;
}
Dışında, bu çirkin görünüyor ve okumak çok zor. Şimdi 2010'da olmayanlara yardımcı olmak için işlev işaretçileri için de kullandığım bir şey kullanıyorum:
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;
}
Bu, türün olmasını beklediği yerde hareket eder ve bu kadar ileri gider daha okunabilir. Tabii ki, 5 element dışında bir şey kullanmayacaksanız bir şablon kullanmak gereksizdir, bu yüzden elbette zor kodlayabilirsiniz:
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;
}
Dediğim gibi benim type_t<>
bu soru sorulduğunda hilem işe yaramazdı. O zamanlar için umabileceğiniz en iyi şey bir yapıda bir tür kullanmaktı:
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;
}
Bu yine oldukça çirkin görünmeye başlar, ancak en azından yine de daha okunabilir typename
, derleyiciye bağlı olarak isteğe bağlı olabilir, sonuç:
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;
}
Ve sonra elbette yardımcımı kullanmak yerine belirli bir tür belirtebilirdiniz.
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;
}
O zamanlar serbest fonksiyonlar std::begin()
ve std::end()
mevcut değildi, ancak kolayca uygulanabilirdi. Bu, bir C dizisinde anlamlı oldukları, ancak bir işaretçi olmadığı için dizi üzerinde daha güvenli bir şekilde yinelemeye izin verirdi.
Diziye erişmeye gelince, onu aynı parametre türünü alan başka bir işleve geçirebilir veya diziye bir takma ad (bu kapsamda orijinaline sahip olduğunuz kadar anlamlı olmaz) olabilir. Dizi referansına erişmek, orijinal diziye erişmek gibidir.
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
veya
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
}
Özetlemek gerekirse, eğer bir dizi üzerinde yineleme yapmak istiyorsanız, bir dizi işaretçisinin bir işaretçiye dönüşmesine izin vermemek en iyisidir. Bu sadece kötü bir fikir çünkü derleyicinin sizi ayağınızdan çekmekten korumasını ve kodunuzu okumayı zorlaştırıyor. Bunu yapmamanız için çok iyi bir nedeniniz yoksa türleri mümkün olduğunca uzun süre tutarak derleyicinin size yardımcı olmasına çalışın.
Düzenle
Oh, ve bütünlük için, bir işaretçiye indirgenmesine izin verebilirsiniz, ancak bu diziyi sahip olduğu öğe sayısından ayırır. Bu C / C ++ 'da çok yapılır ve genellikle dizideki elemanların sayısı geçirilerek hafifletilir. Ancak, bir hata yaparsanız ve öğe sayısına yanlış değer iletirseniz derleyici size yardımcı olamaz.
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
Boyutu iletmek yerine, dizinizin sonunu geçecek bir bitiş işaretçisini iletebilirsiniz. Bu, başlangıç ve bitiş işaretçisini alan std algoritmalarına daha yakın olan bir şey için yararlı olduğu için yararlıdır, ancak geri döndüğünüz şey artık yalnızca hatırlamanız gereken bir şeydir.
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
Alternatif olarak, bu işlevin yalnızca 5 öğe alacağını belgeleyebilir ve işlevinizin kullanıcısının aptalca bir şey yapmamasını umabilirsiniz.
// 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;
}
Dönüş değerinin orijinal türünü kaybettiğini ve bir işaretçiye indirildiğini unutmayın. Bu nedenle, diziyi aşmayacağınızdan emin olmak için artık kendi başınızasınız.
std::pair<int*, int*>
Başlamak ve bitirmek için kullanabileceğiniz bir a geçebilirsiniz ve bunu geçebilirsiniz, ancak sonra gerçekten bir dizi gibi görünmeyi bırakır.
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];
}
veya
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])));
}
Yeterince komik, bu nasıl std::initializer_list
çalıştığına çok benziyor (c ++ 11), ancak bu bağlamda çalışmıyorlar.