У вас уже есть несколько ответов для C и ответ для C ++, но есть другой способ сделать это в C ++.
Как сказал Наваз, чтобы передать массив размером N, вы можете сделать это в C ++:
const size_t N = 16; // For your question.
void foo(int (&arr)[N]) {
// Do something with arr.
}
Однако, начиная с C ++ 11, вы также можете использовать контейнер std :: array, который можно передавать с более естественным синтаксисом (при условии некоторого знакомства с синтаксисом шаблона).
#include <array>
const size_t N = 16;
void bar(std::array<int, N> arr) {
// Do something with arr.
}
Как контейнер, std :: array предоставляет в основном те же функциональные возможности, что и обычный массив в стиле C, но также добавляет дополнительные функциональные возможности.
std::array<int, 5> arr1 = { 1, 2, 3, 4, 5 };
int arr2[5] = { 1, 2, 3, 4, 5 };
// Operator[]:
for (int i = 0; i < 5; i++) {
assert(arr1[i] == arr2[i]);
}
// Fill:
arr1.fill(0);
for (int i = 0; i < 5; i++) {
arr2[i] = 0;
}
// Check size:
size_t arr1Size = arr1.size();
size_t arr2Size = sizeof(arr2) / sizeof(arr2[0]);
// Foreach (C++11 syntax):
for (int &i : arr1) {
// Use i.
}
for (int &i : arr2) {
// Use i.
}
Однако, насколько мне известно (в настоящее время он ограничен), арифметика указателей небезопасна с помощью std :: array, если вы не используете функцию-член data (), чтобы сначала получить фактический адрес массива. Это сделано для предотвращения нарушения вашего кода будущими модификациями класса std :: array, а также потому, что некоторые реализации STL могут хранить дополнительные данные в дополнение к фактическому массиву.
Обратите внимание, что это будет наиболее полезно для нового кода, или если вы преобразуете свой ранее существующий код для использования массивов std :: arrays вместо массивов в стиле C. Поскольку std :: arrays являются агрегатными типами, им не хватает пользовательских конструкторов, и поэтому вы не можете напрямую переключаться с массива в стиле C на std :: array (если не использовать приведение, но это уродливо и может потенциально вызвать проблемы в будущем). ). Чтобы преобразовать их, вам нужно использовать что-то вроде этого:
#include <array>
#include <algorithm>
const size_t N = 16;
std::array<int, N> cArrayConverter(int (&arr)[N]) {
std::array<int, N> ret;
std::copy(std::begin(arr), std::end(arr), std::begin(ret));
return ret;
}
Поэтому, если в вашем коде используются массивы в стиле C, и было бы невозможно преобразовать его в использование std :: arrays, вам лучше придерживаться массивов в стиле C.
(Примечание: я указал размеры как N, чтобы вам было проще использовать код там, где он вам нужен.)
Редактировать: Я забыл упомянуть несколько вещей:
1) Большинство функций стандартной библиотеки C ++, предназначенных для работы с контейнерами, не зависят от реализации; вместо того, чтобы быть разработанными для определенных контейнеров, они работают на диапазонах, используя итераторы. (Это также означает, что они работают для std::basic_string
и его экземпляров, таких как std::string
.) Например, std::copy
имеет следующий прототип:
template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result);
// first is the beginning of the first range.
// last is the end of the first range.
// result is the beginning of the second range.
Хотя это может показаться внушительным, обычно вам не нужно указывать параметры шаблона, и вы можете просто позволить компилятору обработать это для вас.
std::array<int, 5> arr1 = { 1, 2, 3, 4, 5 };
std::array<int, 5> arr2 = { 6, 7, 8, 9, 0 };
std::string str1 = ".dlrow ,olleH";
std::string str2 = "Overwrite me!";
std::copy(arr1.begin(), arr1.end(), arr2.begin());
// arr2 now stores { 1, 2, 3, 4, 5 }.
std::copy(str1.begin(), str1.end(), str2.begin());
// str2 now stores ".dlrow ,olleH".
// Not really necessary for full string copying, due to std::string.operator=(), but possible nonetheless.
Благодаря использованию итераторов эти функции также совместимы с массивами в стиле C (поскольку итераторы являются обобщением указателей, все указатели являются итераторами по определению (но не все итераторы обязательно являются указателями)). Это может быть полезно при работе с устаревшим кодом, поскольку это означает, что у вас есть полный доступ к функциям диапазона в стандартной библиотеке.
int arr1[5] = { 4, 3, 2, 1, 0 };
std::array<int, 5> arr2;
std::copy(std::begin(arr1), std::end(arr1), std::begin(arr2));
Вы, возможно, заметили из этого и последнего примера, что std::array.begin()
и std::begin()
могут использоваться взаимозаменяемо с std::array
. Это связано с тем, что std::begin()
и std::end()
реализованы таким образом, что для любого контейнера они имеют одинаковый тип возврата и возвращают то же значение, что и при вызове функций-членов begin()
и end()
экземпляра этого контейнера.
// Prototype:
template <class Container>
auto begin (Container& cont) -> decltype (cont.begin());
// Examples:
std::array<int, 5> arr;
std::vector<char> vec;
std::begin(arr) == arr.begin();
std::end(arr) == arr.end();
std::begin(vec) == vec.begin();
std::end(vec) == vec.end();
// And so on...
Массивы в стиле C не имеют функций-членов, поэтому для них необходимо использовать std::begin()
и std::end()
. В этом случае две функции перегружены для предоставления применимых указателей в зависимости от типа массива.
// Prototype:
template <class T, size_t N>
T* begin (T(&arr)[N]);
// Examples:
int arr[5];
std::begin(arr) == &arr[0];
std::end(arr) == &arr[4];
Как общее практическое правило, если вы не уверены в том, придется ли конкретному сегменту кода использовать массивы в стиле C, безопаснее использовать std::begin()
и std::end()
.
[Обратите внимание, что хотя я использовал std::copy()
в качестве примера, использование диапазонов и итераторов очень распространено в стандартной библиотеке. Большинство, если не все, функции, предназначенные для работы с контейнерами (или, более конкретно, любая реализация концепции Контейнер , например, std::array
, std::vector
и std::string
) используют диапазоны, делая их совместим с любыми текущими и будущими контейнерами, а также с массивами в стиле C. Однако в этой широко распространенной совместимости могут быть исключения, о которых я не знаю.]
2) При передаче std :: array по значению могут быть значительные издержки, в зависимости от размера массива.Поэтому лучше передать его по ссылке или использовать итераторы (например, стандартную библиотеку).
// Pass by reference.
const size_t N = 16;
void foo(std::array<int, N>& arr);
3) Во всех этих примерах предполагается, что все массивы в вашем коде будут иметь одинаковый размер,как указано константой N
.Чтобы сделать ваш код более независимым от реализации, вы можете либо использовать диапазоны и итераторы самостоятельно, либо, если вы хотите, чтобы ваш код был сосредоточен на массивах, использовать шаблонные функции.[Построение этого ответа на другой вопрос .]
template<size_t SZ> void foo(std::array<int, SZ>& arr);
...
std::array<int, 5> arr1;
std::array<int, 10> arr2;
foo(arr1); // Calls foo<5>(arr1).
foo(arr2); // Calls foo<10>(arr2).
Если вы сделаете это, вы можете даже пойти так далеко, чтобы шаблонировать тип элемента массива, при условии, что ваш код может работатьдля типов, отличных от int.
template<typename T, size_t SZ>
void foo(std::array<T, SZ>& arr);
...
std::array<int, 5> arr1;
std::array<float, 7> arr2;
foo(arr1); // Calls foo<int, 5>(arr1).
foo(arr2); // Calls foo<float, 7>(arr2).
Пример этого в действии см. здесь .
Если кто-то обнаружит какие-либо ошибки, которые я, возможно, пропустилНе стесняйтесь указывать на них, чтобы я их исправлял, или исправлял их самостоятельно.Я думаю, что поймал их всех, но я не уверен на 100%.