Существует ли стандартная функция в C, которая вернет длину массива?

Есть ли стандартная функция в C, которая вернет длину массива?

Ответ 1

Часто техника, описанная в других ответах, инкапсулируется в макрос, чтобы облегчить глаза. Что-то вроде:

#define COUNT_OF( arr) (sizeof(arr)/sizeof(0[arr]))

Обратите внимание, что макрос выше использует небольшой трюк для размещения имени массива в операторе индекса ('[]') вместо 0 - это делается в случае, если макрос ошибочно используется в коде на С++ с который перегружает operator[](). Компилятор будет жаловаться, а не давать плохие результаты.

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

Недавно я начал использовать более сложную версию, которую я украл из Google Chromium codebase:

#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

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

Этот макрос не закрывает дверь, используя его ошибочно, но он приближается, как я когда-либо видел в прямом C.

Если вы хотите получить более безопасное решение, когда работаете на С++, посмотрите Время компиляции sizeof_array без использования макроса, в котором описывается довольно сложный шаблон на основе Microsoft использует в winnt.h.

Ответ 2

Нет, нет.

Для массивов с постоянным размером вы можете использовать общий трюк, о котором упоминал Andrew, sizeof(array) / sizeof(array[0]) - но это работает только в области, в которой был объявлен массив.
sizeof(array) дает размер всего массива, а sizeof(array[0]) дает размер первого элемента.
См. ответ Michaels о том, как обернуть это в макрос.

Для динамически распределенных массивов вы либо отслеживаете размер в интегральном типе, либо, если это возможно, оканчиваете на 0 (т.е. выделяете еще 1 элемент и устанавливаете последний элемент равным 0).

Ответ 3

sizeof array / sizeof array[0]

Ответ 4

Число элементов в массиве x может быть получено:

sizeof(x)/sizeof(x[0])

Вам нужно знать, что массивы при передаче функций деградируют в указатели, которые не содержат информацию о размере. В действительности информация о размере никогда не доступна для среды выполнения, поскольку она вычисляется во время компиляции, но вы можете действовать так, как если бы она была доступна, когда массив видим (т.е. Где он не был деградирован).

Когда я передаю массивы функции, которую мне нужно рассматривать как массивы, я всегда гарантирую, что переданы два аргумента:

  • длина массива; и
  • указатель на массив.

Итак, в то время как массив можно рассматривать как массив, где он объявлен, он рассматривается как размер и указатель везде.

Я имею тенденцию иметь код вроде:

#define countof(x) (sizeof(x)/sizeof(x[0]))
: : :
int numbers[10];
a = fn (countof(numbers),numbers);

тогда fn() будет иметь доступную информацию о размере.

Еще один трюк, который я использовал в прошлом (немного беспорядочный, по моему мнению, но я приведу его здесь для полноты), должен иметь массив объединения и сделать первый элемент длиной, что-то вроде:

typedef union {
    int len;
    float number;
} tNumber;
tNumber number[10];
: : :
number[0].len = 5;
a = fn (number);

то fn() может получить доступ к длине и всем элементам, и вам не нужно беспокоиться о дихотомии массива/указателя.

Это имеет дополнительное преимущество, позволяющее изменять длину (т.е. количество используемых элементов, а не количество выделенных единиц). Но я предпочитаю больше не использовать это, так как лучше рассмотреть версию массива с двумя аргументами (размер и данные).

Ответ 5

Простой ответ, конечно, нет. Но практический ответ: "Мне все равно нужно знать", поэтому давайте обсудим методы работы с этим.

Один способ уйти с ним на некоторое время, как уже упоминалось около миллиона раз, - это sizeof():

int i[] = {0, 1, 2};
...
size_t i_len = sizeof(i) / sizeof(i[0]);

Это работает, пока мы не попытаемся передать i функции, или возьмем указатель на i. Итак, что относительно более общих решений?

Принятое общее решение - передать длину массива функции вместе с массивом. Мы видим это в стандартной библиотеке:

void *memcpy(void *s1, void *s2, size_t n);

Скопирует n байты от s1 до s2, что позволит нам использовать n, чтобы наши буферы никогда не переполнялись. Это хорошая стратегия - у нее низкие накладные расходы, и на самом деле она генерирует эффективный код (сравните с strcpy(), который должен проверить конец строки и не имеет способа "знать", сколько итераций он должен сделать, и плохой confused strncpy(), который должен проверять оба - оба могут быть медленнее, и либо можно ускорить, используя memcpy(), если по какой-то причине вы уже рассчитали длину строки.)

Другой подход заключается в инкапсуляции кода в struct. Обычным взломом является следующее:

typedef struct _arr {
  size_t len;
  int arr[0];
} arr;

Если нам нужен массив длиной 5, мы делаем следующее:

arr *a = malloc(sizeof(*a) + sizeof(int) * 5);
a->len = 5;

Однако это взлома, который только умеренно хорошо определен (C99 позволяет использовать int arr[]) и является довольно трудоемким. "Лучше определенный" способ сделать это:

typedef struct _arr {
  size_t len;
  int *arr;
} arr;

Но тогда наши распределения (и освобождения) становятся намного сложнее. Преимущество любого из этих подходов, конечно же, в том, что теперь массивы, которые вы делаете, будут нести вокруг их длины с ними. Он немного менее эффективен с точки зрения памяти, но он вполне безопасен. Если вы выбрали один из этих путей, не забудьте написать вспомогательные функции, чтобы вам не приходилось вручную выделять и освобождать (и работать) с этими структурами.

Ответ 6

Если у вас есть объект a типа массива, количество элементов в массиве может быть выражено как sizeof a / sizeof *a. Если вы позволили вашему объекту массива распасться на тип указателя (или у него был только объект-указатель), тогда в общем случае нет способа определить количество элементов в массиве.