Как надежно получить размер массива C-стиля?

Как я могу получить размер массива C-стиля? Обычно рекомендуется использовать метод sizeof, но он не работает в функции foo, где x передается в:

#include <iostream>

void foo(int x[]) {
  std::cerr << (sizeof(x) / sizeof(int)); // 2  
}

int main(){
    int x[] = {1,2,3,4,5};
    std::cerr << (sizeof(x) / sizeof(int)); // 5                              
    foo(x);
    return 0;
}

Ответ на этот вопрос рекомендую sizeof, но они не говорят, что он (видимо?) не работает, если вы передаете массив вокруг. Итак, нужно ли вместо этого использовать часовое? (Я не думаю, что пользователям моей функции foo всегда можно доверять, чтобы поставить сторожевую панель в конец. Конечно, я мог бы использовать std::vector, но тогда я не получаю хороший сокращенный синтаксис {1,2,3,4,5}.)

Ответ 1

В параметрах массива C в C на самом деле просто указатели, поэтому sizeof() не будет работать. Вам либо нужно передать размер в качестве другого параметра, либо использовать дозорный пункт - в зависимости от того, что наиболее подходит для вашего дизайна.

Некоторые другие опции:

Дополнительная информация:

  • для С++ вместо передачи указателя необработанного массива вы можете захотеть, чтобы параметр использовал то, что обертывает массив в шаблоне класса, который отслеживает размер массива и предоставляет методы для копирования данных в массив безопасным образом. Что-то вроде шаблон STLSoft array_proxy или Boost boost:: array может помочь. Я использовал шаблон array_proxy для приятного эффекта раньше. Внутри функции, использующей этот параметр, вы получаете std::vector подобные операции, но вызывающий объект может использовать простой массив C. Там нет копирования массива - шаблон array_proxy заботится о том, чтобы упаковать указатель массива и размер массива почти автоматически.

  • макрос для использования в C для определения количества элементов в массиве (например, когда sizeof() может помочь - то есть, вы не имеете дело с простым указателем): Есть ли стандартная функция в C, которая вернет длину массива?

Ответ 2

A общая идиома, упомянутая в документации GNU LibstdС++, - это функция lengthof:

template<typename T, unsigned int sz>
inline unsigned int lengthof(T (&)[sz]) { return sz; }

Вы можете использовать его как

int x[] = {1,2,3,4,5};
std::cerr << lengthof(x) << std::endl;

Предупреждение: это будет работать только тогда, когда массив не разложился в указатель.

Ответ 3

Вы можете либо обойти размер, использовать часовое, либо даже лучше использовать std::vector. Даже если std::vector не хватает списков инициализаторов, все же легко построить вектор с набором элементов (хотя и не совсем так же хорошо)

static const int arr[] = {1,2,3,4,5};
vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );

Класс std::vector также делает ошибки намного сложнее, что стоит своего веса в золоте. Еще один бонус заключается в том, что все С++ должны быть знакомы с ним, и большинство приложений на С++ должны использовать std::vector, а не как исходный массив C.

В качестве быстрой заметки С++ 0x добавляет списки инициализаторов

std::vector<int> v = {1, 2, 3, 4};

Вы также можете использовать Boost.Assign, чтобы сделать то же самое, хотя синтаксис немного запутан.

std::vector<int> v = boost::assign::list_of(1)(2)(3)(4);

или

std::vector<int> v;
v += 1, 2, 3, 4;

Ответ 4

c не поддерживает эту поддержку. После того, как массив будет удален из объявленной области, его размер будет потерян.

Вы можете передать размер с помощью массива. Вы можете даже объединить их в структуру, если вы всегда хотите сохранить размер, хотя у вас будут некоторые накладные расходы на книгу.

Ответ 5

Я также согласен с тем, что метод Корвина выше очень хорош.

template <int N>
void foo(int (&x)[N]) 
{
    std::cerr << N;
}

Я не думаю, что кто-то дал действительно вескую причину, почему это не очень хорошая идея. В java, например, мы можем писать такие вещи, как:

int numbers [] = {1, 2, 3, 4};
for(int i = 0; i < numbers.length(); i++)
{
   System.out.println(numbers[i]+"\n");
}

В С++ было бы неплохо вместо

int numbers [] = {1, 2, 3, 4};
int size = sizeof(numbers)/sizeof(int);
for(int i = 0; i < size; i++)
{
    cout << numbers[i] << endl;
}

Мы могли бы сделать еще один шаг и пойти

template <int N>
int size(int (&X)[N])
{
   return N;
}

Или, если это вызывает проблемы, я полагаю, вы могли бы написать явно:

template < int N >
int size(int (&X)[N])
{
   int value = (sizeof(X)/sizeof(X[0]));
   return value;
}

Тогда нам просто нужно идти в основном:

int numbers [] = {1, 2, 3, 4};
for(int i = 0; i < size(numbers); i++)
{
   cout << numbers[i] << endl;
}

имеет смысл для меня: -)

Ответ 6

Как насчет этого?..

template <int N>
void foo(int (&x)[N]) {
    std::cerr << N;
}

Ответ 7

Выражение массива будет иметь тип, неявно преобразованный из "N-element array of T" в "указатель на T", и его значение будет адресом первого элемента в массиве, если выражение массива не является операндом либо sizeof, либо адрес (&), или если выражение массива является строковым литералом, используемым для инициализации другого массива в объявлении. Короче говоря, вы не можете передать массив функции как массив; то, что получает функция, является значением указателя, а не значением массива.

Вы должны передать размер массива в качестве отдельного параметра.

Поскольку вы используете С++, используйте векторы (или какой-либо другой подходящий контейнер STL) вместо массивов в стиле C. Да, вы теряете удобный текстовый синтаксис, но компромисс более чем стоит. Шутки в сторону.

Ответ 8

Вам нужно передать размер вместе с массивом, как это делается во многих библиотечных функциях, например strncpy(), strncmp() и т.д. Извините, это так, как это работает в C:-).

В качестве альтернативы вы можете развернуть свою собственную структуру, например:

struct array {
    int* data;
    int size;
};

и передать его вокруг вашего кода.

Конечно, вы можете использовать std::list или std::vector, если хотите больше С++ -ish.

Ответ 9

С С++ 11 существует очень удобный способ:

static const int array[] = { 1, 2, 3, 6 };
int size = (int)std::distance(std::begin(array), std::end(array))+1;