длина массива в C - PullRequest
       9

длина массива в C

10 голосов
/ 18 января 2010

Я написал функцию array_length так:

int array_length(int a[]){
    return sizeof(a)/sizeof(int);
}

Однако он возвращает 2, когда я сделал

unsigned int len = array_length(arr);
printf ("%i" , len);

где у меня

int arr[] = {3,4,5,6,1,7,2};
int * parr = arr;

Но когда я просто делаю

int k = sizeof(arr)/sizeof(int);
printf("%i", k);

в основной функции возвращает 7.

Как правильно написать функцию array_length и как ее использовать?

Ответы [ 14 ]

16 голосов
/ 18 января 2010

Вычисление длины массива в C в лучшем случае проблематично.

Проблема с вашим кодом выше в том, что когда вы делаете:

int array_length(int a[]){ 
    return sizeof(a)/sizeof(int); 
} 

Вы просто передаете указатель как "a", поэтому sizeof(a) - это sizeof(int*). Если вы работаете в 64-битной системе, вы всегда получите 2 для sizeof(a)/sizeof(int) внутри функции, так как указатель будет 64-битным.

Вы можете (потенциально) сделать это как макрос, а не функцию, но у него есть свои проблемы ... (Это полностью подчеркивает это, поэтому вы получаете то же поведение, что и у вашего блока int k =....)

12 голосов
/ 19 января 2010

Ваша функция не будет работать.C-массивы и C-указатели имеют разные типы, но массив выродится в указатель, если вы посмотрите на него смешно.

В этом случае вы передаете массив как параметр, и он превращается в указательво время вызова вы измеряете sizeof(int *)/sizeof(int).

. Единственный способ выполнить эту работу - использовать макрос:

#define ARRAYSIZE(x) (sizeof(x)/sizeof(*x))

, и он будет работать только в том случае, если x объявляется в этой области как массив, а не как указатель.

8 голосов
/ 18 января 2010

Использовать макрос ...

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

Также будет работать бонус для любого массива типа данных:)

5 голосов
/ 19 января 2010

Простой ответ на ваш вопрос: нет способа написать функцию array_length. Вы могли бы быть в состоянии обойтись без определения макроса, но это зависит от контекста, в котором вы будете использовать макрос.

Вы сделали общую ошибку, путая массивы и указатели в C. В C имя массива, в большинстве случаев , эквивалентно указателю на его первый элемент. Ваша функция array_length получает массив a в таком контексте. Другими словами, невозможно передать массив как массив в C. Вы функционируете так, как если бы он был определен так:

int array_length(int *a){
    return sizeof(a)/sizeof (int);
}

, который, в основном, делит размер int * на размер int. Кроме того, согласно приведенному выше описанию, невозможно узнать размер массива в C в функции.

Теперь, как вы можете правильно определить его размер вне функции? Ответ заключается в том, что оператор sizeof является одним из случаев, когда имя массива не сводится к указателю на его первый элемент. Я объяснил различия более подробно в этом ответе . Также обратите внимание, что, несмотря на внешний вид, sizeof является оператором, а не функцией (как мы только что узнали, он не может быть функцией, потому что тогда он не сможет рассчитать размер массив).

Наконец, чтобы определить размер массива a любого типа T, я предпочитаю:

size_t sz = sizeof a / sizeof a[0];

Выше не зависит от типа: a может быть любого типа выше. В самом деле, вы могли бы даже изменить тип a, и вам не нужно менять вышеуказанное.

5 голосов
/ 18 января 2010

Как правило, невозможно измерить размер массива C. В вашей основной функции компилятор подсчитывает элементы, которые вы написали между скобками, так что вы действительно объявляете int arr[7]. Это имеет размер, который вы ожидаете.

Однако в вашей функции int a[] эквивалентно int *a - указателю на целое число. Вы знаете, что это массив, поэтому за ним следует больше целых чисел, но ваша array_length функция может быть передана любой целочисленный указатель, поэтому не может знать .

Это одна из многих причин использовать std::vector вместо необработанных массивов, когда это возможно.

3 голосов
/ 19 января 2010

Проблема в том, что параметры функции не могут быть массивами, хотя C позволяет вам сделать объявление, похожее на единицу.Параметр заканчивается простым указателем.Я сказал в другом месте :

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

Вот почему этот тип конструкции, возвращающей количество элементов в массиве, в конечном итоге становится макросом в Си.предыдущий ответ SO на то, что я считаю хорошей (если сложной) реализацией макроса:

Для простоты, вот макрос:

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

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

3 голосов
/ 19 января 2010

попробуйте _count, он определен в WinNT.h как

// Return the number of elements in a statically sized array.
//   DWORD Buffer[100];
//   RTL_NUMBER_OF(Buffer) == 100
// This is also popularly known as: NUMBER_OF, ARRSIZE, _countof, NELEM, etc.
//
#define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0]))
2 голосов
/ 19 января 2010

нет способа определить размер массива, передаваемого в функцию, подобную этой

 void foo(int a[]);

недостаточно информации во время компиляции или во время выполнения, чтобы ее обработать

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

2 голосов
/ 19 января 2010

int arr [что угодно] в списке аргументов функции определяет указатель , а не массив. Следовательно, информация о длине теряется навсегда.

почему!? * * 1005

Чтобы понять почему, вы должны понимать, что такое С. C никогда не копирует сложные фрагменты неявно. Поэтому, когда вы говорите «передайте мне массив», это на самом деле означает «я хочу передать адрес, который обычно называется именем массива».

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

2 голосов
/ 19 января 2010

Общая процедура для вычисления количества элементов в массиве: sizeof arr / sizeof arr[0] (или sizeof arr / sizeof *arr). Сказав это ...

Написание функции для вычисления длины массива, переданного в качестве аргумента, обречено на неудачу, потому что функция получает указатель, а не массив. Когда вы вызываете свою функцию с выражением массива в качестве аргумента, выражение массива будет неявно преобразовано из «массива T» в «указатель на T», и его значение будет установлено так, чтобы оно указывало на первый элемент в массиве. Ваша функция не видит объект массива; он видит указатель.

В контексте объявления параметров функции int a[] точно так же, как int *a, но это только верно для объявлений параметров функции (ничто не сделало бы меня счастливее, чем увидеть первую форму изгнан из всех будущих версий C, но этого не произойдет).

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