Как надежно получить размер массива в стиле C? - PullRequest
10 голосов
/ 08 марта 2010

Как мне надежно получить размер массива в стиле 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} .)

Ответы [ 9 ]

15 голосов
/ 08 марта 2010

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

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

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

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

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

4 голосов
/ 08 марта 2010

A общая идиома , упомянутая в документации 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;

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

3 голосов
/ 08 марта 2010

Вы можете передать размер, использовать часовой или даже лучше использовать 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;
2 голосов
/ 15 марта 2014

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

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");
}

В C ++ было бы неплохо вместо того, чтобы говорить

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;
}

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

2 голосов
/ 08 марта 2010

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

template <int N>
void foo(int (&x)[N]) {
    std::cerr << N;
}
2 голосов
/ 08 марта 2010

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

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

1 голос
/ 09 марта 2010

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

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

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

0 голосов
/ 30 января 2016

Начиная с c ++ 11, есть очень удобный способ:

static const int array[] = { 1, 2, 3, 6 };
int size = (int)std::distance(std::begin(array), std::end(array))+1;
0 голосов
/ 08 марта 2010

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...