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

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

#include 

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}.)

10
задан Community 23 May 2017 в 12:34
поделиться

7 ответов

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

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

Другая информация:

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

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

15
ответ дан 3 December 2019 в 15:06
поделиться

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

template <int N>
void foo(int (&x)[N]) {
    std::cerr << N;
}
3
ответ дан 3 December 2019 в 15:06
поделиться

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

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

2
ответ дан 3 December 2019 в 15:06
поделиться

Общая идиома , упомянутая в документации GNU Libstdc ++, - это функция 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;

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

4
ответ дан 3 December 2019 в 15:06
поделиться

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

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

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

1
ответ дан 3 December 2019 в 15:06
поделиться

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

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

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

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

Конечно, вы все еще можете использовать std :: list или std :: vector , если хотите быть более ориентированными на C ++.

0
ответ дан 3 December 2019 в 15:06
поделиться

Вы можете либо передать размер, либо использовать дозорный, либо, что еще лучше, использовать 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 также значительно усложняет возможность ошибок, что стоит своего веса. в золоте. Еще один бонус заключается в том, что с ним должен быть знаком весь C ++, и большинство приложений C ++ должны использовать std :: vector, а не необработанный массив C.

Вкратце: 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;
3
ответ дан 3 December 2019 в 15:06
поделиться