Đây là một câu hỏi khá cũ, nhưng tôi sẽ đặt vào 2 xu của mình vì có rất nhiều câu trả lời, nhưng không có câu trả lời nào cho thấy tất cả các phương pháp có thể một cách rõ ràng và súc tích (không chắc chắn về bit ngắn gọn, vì điều này có một bit ra khỏi tay. TL; DR).
Tôi giả định rằng OP muốn trả về mảng đã được truyền vào mà không cần sao chép vì một số phương tiện truyền trực tiếp này cho người gọi để được chuyển đến một chức năng khác để làm cho mã trông đẹp hơn.
Tuy nhiên, để sử dụng một mảng như thế này là để cho nó phân rã thành một con trỏ và trình biên dịch coi nó như một mảng. Điều này có thể dẫn đến các lỗi tinh vi nếu bạn truyền vào một mảng như thế, với chức năng hy vọng rằng nó sẽ có 5 phần tử, nhưng trình gọi của bạn thực sự chuyển qua một số khác.
Có một vài cách bạn có thể xử lý việc này tốt hơn. Chuyển qua một std::vector
hoặc std::array
(không chắc chắn std::array
là khoảng năm 2010 khi câu hỏi được hỏi). Sau đó, bạn có thể truyền đối tượng làm tham chiếu mà không cần sao chép / di chuyển đối tượng.
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;
}
Tuy nhiên, nếu bạn khăng khăng chơi với mảng C, thì hãy sử dụng một mẫu sẽ giữ thông tin có bao nhiêu mục trong mảng.
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;
}
Ngoại trừ, nó trông mông xấu xí, và siêu khó đọc. Bây giờ tôi sử dụng một cái gì đó để giúp với những thứ không có trong năm 2010, mà tôi cũng sử dụng cho các con trỏ hàm:
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;
}
Này di chuyển loại nơi người ta mong chờ nó được, làm cho này xa dễ đọc hơn. Tất nhiên, sử dụng một mẫu là không cần thiết nếu bạn sẽ không sử dụng bất cứ thứ gì ngoài 5 yếu tố, do đó, tất nhiên bạn có thể mã cứng nó:
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;
}
Như tôi đã nói, type_t<>
mánh khóe của tôi sẽ không có tác dụng vào thời điểm câu hỏi này được hỏi. Điều tốt nhất bạn có thể hy vọng trở lại sau đó là sử dụng một loại trong một cấu trúc:
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;
}
Cái này bắt đầu trông khá xấu xí một lần nữa, nhưng ít nhất vẫn dễ đọc hơn, mặc dù typename
có thể là tùy chọn trở lại sau đó tùy thuộc vào trình biên dịch, dẫn đến:
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;
}
Và dĩ nhiên sau đó bạn có thể đã chỉ định một loại cụ thể, thay vì sử dụng trình trợ giúp của tôi.
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;
}
Trước đó, các chức năng miễn phí std::begin()
và std::end()
không tồn tại, mặc dù có thể dễ dàng thực hiện. Điều này sẽ cho phép lặp qua mảng một cách an toàn hơn vì chúng có ý nghĩa trên mảng C, nhưng không phải là một con trỏ.
Đối với việc truy cập mảng, bạn có thể chuyển nó sang một hàm khác có cùng loại tham số hoặc tạo bí danh cho nó (điều này sẽ không có ý nghĩa nhiều như bạn đã có bản gốc trong phạm vi đó). Truy cập một tham chiếu mảng cũng giống như truy cập vào mảng ban đầu.
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
hoặc là
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
}
Tóm lại, tốt nhất là không cho phép một mảng phân rã thành một con trỏ nếu bạn có ý định lặp lại nó. Nó chỉ là một ý tưởng tồi vì nó giữ cho trình biên dịch bảo vệ bạn khỏi bị bắn vào chân và làm cho mã của bạn khó đọc hơn. Luôn cố gắng và giúp trình biên dịch giúp bạn bằng cách giữ các loại càng lâu càng tốt trừ khi bạn có lý do rất chính đáng để không làm như vậy.
Biên tập
Ồ, và để hoàn thiện, bạn có thể cho phép nó biến thành một con trỏ, nhưng điều này tách rời mảng khỏi số phần tử mà nó giữ. Điều này được thực hiện rất nhiều trong C / C ++ và thường được giảm thiểu bằng cách chuyển số lượng phần tử trong mảng. Tuy nhiên, trình biên dịch không thể giúp bạn nếu bạn mắc lỗi và chuyển sai giá trị cho số phần tử.
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
Thay vì vượt qua kích thước, bạn có thể vượt qua con trỏ kết thúc, nó sẽ trỏ đến một điểm qua cuối mảng của bạn. Điều này rất hữu ích vì nó làm cho một cái gì đó gần với các thuật toán tiêu chuẩn hơn, lấy con trỏ bắt đầu và kết thúc, nhưng những gì bạn trả về bây giờ chỉ là thứ bạn phải nhớ.
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
Ngoài ra, bạn có thể tài liệu rằng chức năng này sẽ chỉ có 5 yếu tố và hy vọng rằng người dùng chức năng của bạn sẽ không làm điều gì ngu ngốc.
// 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;
}
Lưu ý rằng giá trị trả về đã mất loại ban đầu và bị biến thành con trỏ. Vì điều này, giờ đây bạn phải tự mình đảm bảo rằng bạn sẽ không vượt qua mảng.
Bạn có thể vượt qua một std::pair<int*, int*>
cái mà bạn có thể sử dụng để bắt đầu và kết thúc và vượt qua nó, nhưng sau đó nó thực sự dừng lại trông giống như một mảng.
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];
}
hoặc là
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])));
}
Hài hước lắm, điều này rất giống với cách std::initializer_list
làm việc (c ++ 11), nhưng chúng không hoạt động trong bối cảnh này.