Чтобы определить размер вашего массива в байтах, вы можете использовать sizeof
оператор:
int a[17];size_t n =sizeof(a);
На моем компьютере длина целых 4 байта, поэтому n равно 68.
Чтобы определить количество элементов в массиве, мы можем разделить общий размер массива на размер элемента массива. Вы можете сделать это с типом, как это:
int a[17];size_t n =sizeof(a)/sizeof(int);
и получите правильный ответ (68/4 = 17), но если тип
aизменится, у вас будет неприятная ошибка, если вы забудете изменить sizeof(int)также.
Поэтому предпочтительным делителем является sizeof(a[0])или эквивалентен sizeof(*a)размер первого элемента массива.
int a[17];size_t n =sizeof(a)/sizeof(a[0]);
Еще одним преимуществом является то, что теперь вы можете легко параметризовать имя массива в макросе и получить:
#define NELEMS(x)(sizeof(x)/sizeof((x)[0]))int a[17];size_t n = NELEMS(a);
Сгенерированный код будет идентичен, так как компилятор знает тип * int_arr во время компиляции (и, следовательно, значение sizeof (* int_arr)). Это будет константа, и компилятор может оптимизировать соответственно.
Марк Харрисон
10
Так должно быть со всеми компиляторами, поскольку результаты sizeof определяются как константа времени компиляции.
Марк Харрисон
451
Важно : не прекращайте читать здесь, прочитайте следующий ответ! Это работает только для массивов в стеке , например, если вы используете malloc () или обращаетесь к параметру функции, вам не повезло. См. ниже.
Маркус
7
Для программирования Windows API на C или C ++ есть ARRAYSIZEопределенный макрос WinNT.h(который добавляется другими заголовками). Таким образом, пользователям WinAPI не нужно определять свои собственные макро.
Луми
17
@Markus работает для любой переменной, имеющей тип массива; это не должно быть "в стеке". Например static int a[20];. Но ваш комментарий полезен для читателей, которые могут не осознавать разницу между массивом и указателем.
ММ
808
Это sizeofправильный путь, если вы имеете дело с массивами, не полученными в качестве параметров. Массив, отправленный в качестве параметра функции, рассматривается как указатель, поэтому sizeofон возвращает размер указателя, а не размер массива.
Таким образом, внутри функции этот метод не работает. Вместо этого всегда передавайте дополнительный параметр, size_t sizeуказывающий количество элементов в массиве.
Тестовое задание:
#include<stdio.h>#include<stdlib.h>void printSizeOf(int intArray[]);void printLength(int intArray[]);int main(int argc,char* argv[]){intarray[]={0,1,2,3,4,5,6};
printf("sizeof of array: %d\n",(int)sizeof(array));
printSizeOf(array);
printf("Length of array: %d\n",(int)(sizeof(array)/sizeof(array[0])));
printLength(array);}void printSizeOf(int intArray[]){
printf("sizeof of parameter: %d\n",(int)sizeof(intArray));}void printLength(int intArray[]){
printf("Length of parameter: %d\n",(int)(sizeof(intArray)/sizeof(intArray[0])));}
Вывод (в 64-битной ОС Linux):
sizeof of array:28sizeof of parameter:8Length of array:7Length of parameter:2
Вывод (в 32-битной ОС Windows):
sizeof of array:28sizeof of parameter:4Length of array:7Length of parameter:1
почему, length of parameter:2если передается только указатель на первый элемент массива?
Bbvarghe
16
@Bbvarghe Это потому, что указатели в 64-битных системах имеют размер 8 байт (sizeof (intArray)), но длина по-прежнему (обычно) 4 байта (sizeof (intArray [0])).
Elideb
13
@Pacerier: правильного кода нет - обычное решение - передать длину вместе с массивом в качестве отдельного аргумента.
Жан Хоминал
10
Подождите, так что нет способа получить доступ к массиву непосредственно из указателя и увидеть его размер? Новое для C здесь.
Судо
7
@Michael Trouw: вы можете использовать синтаксис оператора , если это заставляет вас чувствовать себя лучше: (sizeof array / sizeof *array).
Chqrlie
134
Стоит отметить, что sizeofэто не помогает при работе со значением массива, который распался на указатель: даже если он указывает на начало массива, для компилятора он такой же, как указатель на один элемент этого массива , Указатель не «запоминает» что-либо еще о массиве, который использовался для его инициализации.
int a[10];int* p = a;
assert(sizeof(a)/sizeof(a[0])==10);
assert(sizeof(p)==sizeof(int*));
assert(sizeof(*p)==sizeof(int));
@ Magnus: стандарт определяет sizeof как число байтов в объекте, а sizeof (char) всегда равен единице. Количество битов в байте зависит от конкретной реализации. Изменить: стандартный раздел ANSI C ++ 5.3.3 Sizeof: «Оператор sizeof возвращает число байтов в объектном представлении своего операнда. Размеры [char], sizeof (знаковый char) и sizeof (неподписанный char) 1; результат sizeof, примененного к любому другому фундаментальному типу, определяется реализацией. "
Skizz
Раздел 1.6 Модель памяти C ++: «Базовой единицей хранения в модели памяти C ++ является байт. Байт, по крайней мере, достаточно большой, чтобы содержать любой элемент базового набора символов выполнения, и состоит из непрерывной последовательности битов, числа из которых определяется реализацией ".
Skizz
2
Я помню, что у CRAY был C с char32 битами. Все, что говорится в стандарте, заключается в том, что могут быть представлены целочисленные значения от 0 до 127, а его диапазон составляет по крайней мере от -127 до 127 (символ подписан) или от 0 до 255 (символ без знака).
vonbrand
5
Это отличный ответ. Я хочу прокомментировать, что все вышеприведенные утверждения оцениваются как ИСТИНА.
Джавад
49
Размер «трюка» - лучший способ, который я знаю, с одним небольшим, но (для меня это главная любимая мозоль) важным изменением в использовании скобок.
Как ясно видно из статьи в Википедии, С sizeofне является функцией; это оператор . Таким образом, он не требует скобок вокруг своего аргумента, если аргумент не является именем типа. Это легко запомнить, поскольку аргумент выглядит как приведенное выражение, которое также использует круглые скобки.
Итак: если у вас есть следующее:
int myArray[10];
Вы можете найти количество элементов с кодом следующим образом:
size_t n =sizeof myArray /sizeof*myArray;
Для меня это читается намного проще, чем альтернатива с круглыми скобками. Я также предпочитаю использовать звездочку в правой части раздела, поскольку она более краткая, чем индексирование.
Конечно, это также время компиляции, поэтому нет необходимости беспокоиться о разделении, влияющем на производительность программы. Так что используйте эту форму везде, где можете.
Всегда лучше использовать sizeof на реальном объекте, когда он у вас есть, а не на типе, поскольку тогда вам не нужно беспокоиться о том, чтобы сделать ошибку и указать неверный тип.
Например, скажем, у вас есть функция, которая выводит некоторые данные в виде потока байтов, например, по сети. Давайте вызовем функцию send()и заставим ее принимать в качестве аргументов указатель на объект для отправки и количество байтов в объекте. Итак, прототип становится:
void send(constvoid*object,size_t size);
А затем вам нужно отправить целое число, поэтому вы кодируете его так:
int foo =4711;
send(&foo,sizeof(int));
Теперь вы ввели тонкий способ выстрелить себе в ногу, указав тип fooв двух местах. Если одно меняется, а другое нет, код ломается. Таким образом, всегда делайте это так:
send(&foo,sizeof foo);
Теперь вы защищены. Конечно, вы дублируете имя переменной, но вероятность того, что компилятор может обнаружить ее, если вы ее измените, высока.
Кстати, они одинаковые инструкции на уровне процессора? Требует sizeof(int)меньших инструкций, чем sizeof(foo)?
Pacerier
@Pacerier: нет, они идентичны. Думай о int x = 1+1;против int x = (1+1);. Здесь круглые скобки сугубо просто эстетичны.
Кетцалькоатль
@Aidiakapi Это не правда, рассмотрим C99 VLA.
расслабиться
@unwind Спасибо, я исправлен. Чтобы исправить мой комментарий, sizeofвсегда будет постоянным в C ++ и C89. С массивами переменной длины C99 это может быть оценено во время выполнения.
Aidiakapi
2
sizeofможет быть оператором, но это следует рассматривать как функцию в соответствии с Линусом Торвальдсом. Согласен. Прочитайте его рациональное здесь: lkml.org/lkml/2012/7/11/103
Малый придира: результат вычитания указателя имеет тип ptrdiff_t. (Обычно в 64-битной системе это будет больший тип, чем int). Даже если вы измените intна ptrdiff_tв этом коде, он все равно имеет ошибку, если arrзанимает более половины адресного пространства.
ММ
2
@MM Еще одна небольшая хитрость: в зависимости от архитектуры вашей системы адресное пространство не так велико, как размер указателя в большинстве систем. Например, Windows ограничивает адресное пространство для 64-битных приложений 8 ТБ или 44 битами. Так что, даже если у вас есть массив, например, больше половины вашего адресного пространства 4,1 ТБ, это не будет ошибкой. Только если ваше адресное пространство превышает 63 бита в этих системах, возможно даже столкнуться с такой ошибкой. В общем, не беспокойся об этом.
Aidiakapi
1
@Aidiakapi в 32-разрядной системе x86 Linux или в Windows с /3Gвозможностью разделения пользователя / ядра 3G / 1G, что позволяет иметь размер массивов до 75% от размера адресного пространства.
Руслан
1
Рассмотрим в foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];качестве глобальных переменных. buf3[]декларация терпит неудачу, поскольку (&buf1)[1] - buf1не является константой.
chux - Восстановить Монику
2
Это технически неопределенное поведение, поскольку стандарт явно запрещает разыменование после конца массива (даже если вы не пытаетесь прочитать сохраненное значение)
ММ
26
Вы можете использовать оператор sizeof, но он не будет работать для функций, поскольку он будет использовать ссылку на указатель. Чтобы найти длину массива, выполните следующие действия:
Если вы знаете тип данных массива, вы можете использовать что-то вроде:
int arr[]={23,12,423,43,21,43,65,76,22};int noofele =sizeof(arr)/sizeof(int);
Или, если вы не знаете тип данных массива, вы можете использовать что-то вроде:
noofele =sizeof(arr)/sizeof(arr[0]);
Примечание. Это работает, только если массив не определен во время выполнения (например, malloc) и массив не передан в функцию. В обоих случаях arr(имя массива) является указателем.
int noofele = sizeof(arr)/sizeof(int);только наполовину лучше, чем кодирование int noofele = 9;. Использование sizeof(arr)поддерживает гибкость при изменении размера массива. Все же sizeof(int)нуждается в обновлении, если тип arr[]изменения. Лучше использовать, sizeof(arr)/sizeof(arr[0])даже если тип хорошо известен. Непонятно, почему используется intдля noofeleпротив size_t, тип, возвращаемый sizeof().
chux - Восстановить Монику
19
Макрос, ARRAYELEMENTCOUNT(x)который все используют, оценивает неправильно . На самом деле, это просто деликатный вопрос, потому что у вас не может быть выражений, которые приводят к типу «массив».
Это действительно не имеет непосредственного отношения к размеру массивов. Я только что заметил много ошибок из-за неправильного наблюдения за тем, как работает препроцессор Си. Вы всегда переносите параметр макроса, а не выражение, в которое может быть вовлечено.
Это правильно; мой пример был плохим. Но это именно то, что должно произойти. Как я упоминал ранее p + 1, в конечном итоге это будет указатель типа и сделает недействительным весь макрос (как если бы вы пытались использовать макрос в функции с параметром указателя).
В конце дня, в данном конкретном случае, ошибка на самом деле не имеет значения (так что я просто трачу время каждого; хазз!), Потому что у вас нет выражений с типом «массив». Но на самом деле вопрос о тонкостях оценки препроцессора, я думаю, является важным.
Спасибо за объяснение. Исходная версия приводит к ошибке во время компиляции. Clang сообщает, что «подписанное значение не является массивом, указателем или вектором». Это кажется предпочтительным поведением в этом случае, хотя ваши комментарии о порядке оценки в макросах хорошо приняты.
Марк Харрисон
1
Я не думал о жалобе компилятора как об автоматическом уведомлении неверного типа. Спасибо!
3
Есть ли причина не использовать (sizeof (x) / sizeof (*x))?
Серьезный
16
Для многомерных массивов это немного сложнее. Часто люди определяют явные макроконстанты, т.е.
В зависимости от типа arrayимеет, вам не нужно использовать , sizeof(array) / sizeof(array[0])если arrayэто массив либо char, unsigned charили signed char- Цитата из C18,6.5.3.4 / 4: «Когда SizeOf применяются к операнду , который имеет тип CHAR, беззнаковый символ или символ подписанный , (или его квалифицированная версия) результат равен 1 ». В этом случае вы можете просто сделать, sizeof(array)как описано в моем ответе .
RobertS поддерживает Монику Челлио
15
Размер массива в C:
int a[10];size_t size_of_array =sizeof(a);// Size of array aint n =sizeof(a)/sizeof(a[0]);// Number of elements in array asize_t size_of_element =sizeof(a[0]);// Size of each element in array a // Size of each element = size of type
Любопытно , что код , используемый size_t size_of_elementеще intс int n = sizeof (a) / sizeof (a[0]); и неsize_t n = sizeof (a) / sizeof (a[0]);
chux - восстановят Монику
1
Привет @Yogeesh HT, не могли бы вы ответить на вопрос о Chux. Мне также очень любопытно узнать, как int n = sizeof (a) / sizeof (a [0]) дает длину массива и почему мы не используем size_t для длины массива. Кто-нибудь может ответить на это?
Мозг
1
@Brain sizeof (a) дает sizeof всех элементов, присутствующих в массиве, а sizeof (a [0]) дает sizeof 1-го элемента. Предположим, а = {1,2,3,4,5}; sizeof (a) = 20 байт (если sizeof (int) = 4 байт умножить на 5), sizeof (a [0]) = 4 байт, поэтому 20/4 = 5, т.е. нет элементов
Йогиш HT
2
@YogeeshHT Для очень больших массивов нравится char a[INT_MAX + 1u];, int nкак он используется в int n = sizeof (a) / sizeof (a[0]);недостаточном (это УБ). Использование size_t n = sizeof (a) / sizeof (a[0]);не влечет за собой этой проблемы.
chux - Восстановить Монику
13
Я бы посоветовал никогда не использовать sizeof(даже если это можно использовать) для получения любого из двух разных размеров массива, либо по количеству элементов, либо в байтах, которые являются двумя последними случаями, которые я здесь показываю. Для каждого из двух размеров макросы, показанные ниже, могут быть использованы для повышения безопасности. Причина заключается в том, чтобы сделать понятным намерение кода для сопровождающих, и отличие sizeof(ptr)от sizeof(arr)на первый взгляд (которое написано таким образом не очевидно), чтобы ошибки были очевидны для всех, кто читает код.
Я не согласен с решением, которое предлагает Линус, которое никогда не использует нотацию массива для параметров функций.
Мне нравится обозначение массива как документация, что указатель используется в качестве массива. Но это означает, что необходимо применять надежное решение, чтобы невозможно было написать код с ошибками.
Из массива у нас есть три размера, которые мы могли бы знать:
Размер элементов массива
Количество элементов в массиве
Размер в байтах, который массив использует в памяти
Размер элементов массива
Первый очень прост, и не имеет значения, имеем ли мы дело с массивом или указателем, потому что это делается так же.
qsort() нужно это значение в качестве третьего аргумента.
Для двух других размеров, которые являются темой вопроса, мы хотим убедиться, что мы имеем дело с массивом, и прервать компиляцию, если нет, потому что, если мы имеем дело с указателем, мы получим неправильные значения , Когда компиляция будет прервана, мы сможем легко увидеть, что мы имеем дело не с массивом, а с указателем, а нам просто нужно написать код с переменной или макросом, который хранит размер массив за указателем.
Количество элементов в массиве
Этот является наиболее распространенным, и многие ответы предоставили вам типичный макрос ARRAY_SIZE:
Массивы с более чем PTRDIFF_MAXодним членом будут давать недопустимые значения для этой подписанной версии макроса, но после чтения C17 :: 6.5.6.9 подобные массивы уже играют с огнем. Только ARRAY_SIZEи size_tследует использовать в этих случаях.
Последние версии компиляторов, такие как GCC 8, будут предупреждать вас, когда вы применяете этот макрос к указателю, поэтому он безопасен (есть другие способы сделать его более безопасным со старыми компиляторами).
Он работает путем деления размера в байтах всего массива на размер каждого элемента.
Примеры использования:
void foo(ptrdiff_t nmemb){char buf[nmemb];
fgets(buf, ARRAY_SIZE(buf), stdin);}void bar(ptrdiff_t nmemb){int arr[nmemb];for(ptrdiff_t i =0; i < ARRAY_SSIZE(arr); i++)
arr[i]= i;}
Если бы эти функции не использовали массивы, а вместо этого получили их в качестве параметров, прежний код не скомпилировался бы, поэтому было бы невозможно иметь ошибку (учитывая, что используется последняя версия компилятора или что используется какой-то другой прием) и нам нужно заменить вызов макроса значением:
void foo(ptrdiff_t nmemb,char buf[nmemb]){
fgets(buf, nmemb, stdin);}void bar(ptrdiff_t nmemb,int arr[nmemb]){for(ptrdiff_t i =0; i < nmemb; i++)
arr[i]= i;}
Размер в байтах, который массив использует в памяти
ARRAY_SIZE обычно используется в качестве решения предыдущего случая, но этот случай редко пишется безопасно, возможно потому, что он менее распространен.
Обычный способ получить это значение - использовать sizeof(arr). Проблема: та же, что и с предыдущей; если у вас есть указатель вместо массива, ваша программа сойдет с ума.
Решение проблемы включает использование того же макроса, что и раньше, который, как мы знаем, безопасен (он нарушает компиляцию, если он применяется к указателю):
Как это работает, очень просто: оно отменяет деление, которое ARRAY_SIZEпроисходит, поэтому после математических отмен вы получите только один sizeof(arr), но с дополнительной безопасностью ARRAY_SIZEконструкции.
Сегодня я узнал, что новое предупреждение в GCC работает, только если макрос определен в заголовке, который не является системным заголовком. Если вы определяете макрос в заголовке, который установлен в вашей системе (обычно /usr/local/include/или /usr/include/) ( #include <foo.h>), компилятор НЕ выдаст предупреждение (я попробовал GCC 9.3.0).
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a),typeof(b))#define is_array(a)(!is_same_type((a),&(a)[0]))#defineStatic_assert_array(a)_Static_assert(is_array(a),"Not a `[]` !")#define ARRAY_SIZE(arr)( \
{ \
Static_assert_array(arr); \
sizeof(arr)/sizeof((arr)[0]); \
} \
)
Теперь ARRAY_SIZE()это абсолютно безопасно, и, следовательно, все его производные будут в безопасности.
Обновление: libbsd предоставляет __arraycount():
Libbsd предоставляет макрос __arraycount()in <sys/cdefs.h>, что небезопасно, поскольку в нем отсутствует пара круглых скобок, но мы можем сами добавить эти скобки, и поэтому нам даже не нужно записывать деление в нашем заголовке (зачем дублировать уже существующий код? ). Этот макрос определен в системном заголовке, поэтому, если мы его используем, мы вынуждены использовать макросы выше.
Некоторые системы обеспечивают nitems()в <sys/param.h>вместо этого, и в некоторых системах обоих. Вы должны проверить свою систему и использовать ту, которая у вас есть, и, возможно, использовать некоторые условные выражения препроцессора для переносимости и поддержки обоих.
Обновление: разрешить использование макроса в области видимости файла:
К сожалению, ({})расширение gcc не может быть использовано на уровне файла. Чтобы иметь возможность использовать макрос в области видимости файла, статическое утверждение должно быть внутри sizeof(struct {}). Затем умножьте его на, 0чтобы не повлиять на результат. Приведение к (int)может быть полезно для имитации возвращаемой функции (int)0(в этом случае это не обязательно, но затем ее можно использовать для других целей).
Не могли бы вы объяснить, почему понизить голос? Это показывает решение небезопасной и общую конструкцию ( sizeof(arr)), не отображаются в другом месте: ARRAY_BYTES(arr).
Cacahuete Frito
2
ARRAY_SIZE достаточно распространен, чтобы его можно было свободно использовать, а ARRAY_BYTES очень явно указан в своем названии и должен быть определен рядом с ARRAY_SIZE, чтобы пользователь мог видеть как легко, так и по его использованию, я не думаю, что кто-то, читающий код, сомневается в оно делает. Я имел в виду не использовать простое sizeof, а использовать вместо этого эти конструкции; если вам хочется писать эти конструкции каждый раз, вы, скорее всего, совершите ошибку (очень часто, если вы копируете вставить, и также очень часто, если вы пишете их каждый раз, потому что у них много скобок) ...
Cacahuete Frito
3
..., поэтому я стою на главном заключении: одно sizeofявно небезопасно (причины в ответе), и не использование макросов, а использование предоставленных мною конструкций, каждый раз, еще более небезопасно, поэтому единственный путь это макросы.
Cacahuete Frito
3
@MarkHarrison Я знаю разницу между указателями и массивами. Но были времена, когда у меня была функция, которую я позже преобразовал в маленькие функции, и что сначала было массивом, потом указателем, и это одна точка, где, если вы забудете изменить sizeof, вы ввернете ее, и это легко не увидеть один из тех.
Какауэте Фрито
3
@hyde Также то, что я знаю разницу, не означает, что все знают разницу, и почему бы не использовать то, что в основном удаляет 100% этих ошибок? Эта ошибка почти превратилась в Linux; он прибыл к Линусу, что означает, что он прошел тщательную проверку, а также означает, что есть вероятность того, что та же самая ошибка превратила его в Linux в другой части ядра, как он говорит.
Какауэте Фрито
11
«Вы ввели тонкий способ выстрелить себе в ногу»
C 'родные' массивы не сохраняют свой размер. Поэтому рекомендуется сохранять длину массива в отдельной переменной / const и передавать ее всякий раз, когда вы передаете массив, то есть:
Вы ДОЛЖНЫ всегда избегать нативных массивов (если вы не можете, в этом случае, следите за своей ногой). Если вы пишете на C ++, используйте контейнер STL 'vector'. «По сравнению с массивами они обеспечивают почти одинаковую производительность», и они гораздо полезнее!
// vector is a template, the <int> means it is a vector of intsvector<int> numbers;// push_back() puts a new value at the end (or back) of the vectorfor(int i =0; i <10; i++)
numbers.push_back(i);// Determine the size of the array
cout << numbers.size();
Обратите внимание, что это работает только для реальных массивов, а не для указателей, которые указывают на массивы.
Дэвид Шварц
5
Если вы действительно хотите сделать это, чтобы обойти массив, я предлагаю реализовать структуру для хранения указателя на тип, для которого вы хотите массив, и целого числа, представляющего размер массива. Затем вы можете передать это своим функциям. Просто назначьте этому указателю значение переменной массива (указатель на первый элемент). Затем вы можете перейти Array.arr[i]к получению i-го элемента и использовать Array.sizeдля получения количества элементов в массиве.
Я включил некоторый код для вас. Это не очень полезно, но вы можете расширить его с помощью дополнительных функций. Если честно, если вы хотите именно этого, вам следует прекратить использовать C и использовать другой язык со встроенными функциями.
/* Absolutely no one should use this...
By the time you're done implementing it you'll wish you just passed around
an array and size to your functions *//* This is a static implementation. You can get a dynamic implementation and
cut out the array in main by using the stdlib memory allocation methods,
but it will work much slower since it will store your array on the heap */#include<stdio.h>#include<string.h>/*
#include "MyTypeArray.h"
*//* MyTypeArray.h
#ifndef MYTYPE_ARRAY
#define MYTYPE_ARRAY
*/typedefstructMyType{int age;char name[20];}MyType;typedefstructMyTypeArray{int size;MyType*arr;}MyTypeArray;MyType new_MyType(int age,char*name);MyTypeArray newMyTypeArray(int size,MyType*first);/*
#endif
End MyTypeArray.h *//* MyTypeArray.c */MyType new_MyType(int age,char*name){MyType d;
d.age = age;
strcpy(d.name, name);return d;}MyTypeArray new_MyTypeArray(int size,MyType*first){MyTypeArray d;
d.size = size;
d.arr = first;return d;}/* End MyTypeArray.c */void print_MyType_names(MyTypeArray d){int i;for(i =0; i < d.size; i++){
printf("Name: %s, Age: %d\n", d.arr[i].name, d.arr[i].age);}}int main(){/* First create an array on the stack to store our elements in.
Note we could create an empty array with a size instead and
set the elements later. */MyType arr[]={new_MyType(10,"Sam"), new_MyType(3,"Baxter")};/* Now create a "MyTypeArray" which will use the array we just
created internally. Really it will just store the value of the pointer
"arr". Here we are manually setting the size. You can use the sizeof
trick here instead if you're sure it will work with your compiler. */MyTypeArrayarray= new_MyTypeArray(2, arr);/* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
print_MyType_names(array);return0;}
Невозможно обновить код strcpy(d.name, name);без обработки переполнения.
chux - Восстановить Монику
4
Лучше всего сохранить эту информацию, например, в структуре:
typedefstruct{int*array;int elements;} list_s;
Реализуйте все необходимые функции, такие как создание, уничтожение, проверка равенства и все остальное, что вам нужно. Проще передать в качестве параметра.
Есть ли причина для int elementsпротив size_t elements?
chux - Восстановить Монику
3
Функция sizeofвозвращает количество байтов, которое используется вашим массивом в памяти. Если вы хотите вычислить количество элементов в вашем массиве, вы должны разделить это число на sizeofтип переменной массива. Допустим int array[10];, если на вашем компьютере переменная типа integer 32-битная (или 4-байтовая), для того, чтобы получить размер вашего массива, вы должны сделать следующее:
Вы можете использовать &оператор. Вот исходный код:
#include<stdio.h>#include<stdlib.h>int main(){int a[10];int*p;
printf("%p\n",(void*)a);
printf("%p\n",(void*)(&a+1));
printf("---- diff----\n");
printf("%zu\n",sizeof(a[0]));
printf("The size of array a is %zu\n",((char*)(&a+1)-(char*)a)/(sizeof(a[0])));return0;};
Вот пример вывода
15492166721549216712---- diff----4The size of array a is 10
Я не понизил голос, но это все равно, что бить гвоздем кирпичом, потому что рядом с тобой не было молотка. Кроме того, люди, как правило, недовольны использованием неинициализированных переменных ... но здесь, я думаю, это служит вашей цели достаточно хорошо.
Дмитрий
2
@Dmitri здесь не доступны неинициализированные переменные
ММ,
1
Хммм. Вычитание указателя приводит к ptrdiff_t. sizeof()результаты в size_t. С не определяет, кто шире или выше / того же ранга. Таким образом, тип фактора ((char *)(&a+1)-(char *)a)/(sizeof(a[0]))не обязательно size_tи, следовательно, печать с zможет привести к UB. Простого использования printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);достаточно.
chux - Восстановить Монику
1
(char *)(&a+1)-(char *)aне является константой и может быть рассчитана во время выполнения, даже с фиксированным размером a[10]. sizeof(a)/sizeof(a[0])в этом случае выполняется постоянно во время компиляции.
Помимо уже предоставленных ответов, я хочу указать на особый случай с использованием
sizeof(a)/sizeof(a[0])
Если aэто либо массив char, unsigned charлибо signed charвам не нужно использовать sizeofдважды, так как sizeofвыражение с одним операндом этих типов всегда приводит к 1.
Цитата из C18,6.5.3.4 / 4:
« Когда sizeofприменяется к операнду, который имеет тип char, unsigned charили signed char(или его квалифицированную версию), результат будет 1».
Таким образом, sizeof(a) / sizeof (a[0])было бы эквивалентно, NUMBER OF ARRAY ELEMENTS / 1если aэто массив типа char, unsigned charили signed char. Деление на 1 является излишним.
В этом случае вы можете просто сократить и сделать:
sizeof(a)
Например:
char a[10];size_t length =sizeof(a);
Если вы хотите доказательства, вот ссылка на GodBolt .
Тем не менее, подразделение поддерживает безопасность, если тип значительно изменяется (хотя эти случаи редки).
Вы, вероятно, предпочитаете по-прежнему применять макрос с разделением, потому что тип может измениться в будущем (хотя, возможно, и маловероятно), и разделение известно во время компиляции, поэтому компилятор его оптимизирует (если он не будет изменен) твой компилятор).
Какауэте Фрито
1
@CacahueteFrito Да, я тоже об этом думал. Я принял это как примечание к ответу. Спасибо.
RobertS поддерживает Монику Челлио
-1
Примечание: это может дать вам неопределенное поведение, как указано ММ в комментарии.
int a[10];int size =(*(&a+1)-a);
Для более подробной информации смотрите
здесь,
а также здесь .
Это технически неопределенное поведение; *оператор не может быть применен к пришедшим к конечному указателю
MM
3
«неопределенное поведение» означает, что стандарт C не определяет поведение. Если вы попробуете это в своей программе, тогда может произойти все что угодно
MM
@MM вы говорите *(&a+1) - a;, отличается от (&a)[1] - a;выше, не так *(&a+1)и (&a)[1]считаются 1 в конце прошлого?
QuentinUK
@QuentinUK ваши два выражения одинаковы, x[y]определяется как*(x + (y))
ММ
@ ММ, я так и думал. Но другой ответ Арджуна Шридхарана имеет 38 стрелок вверх, а это -1. И в ответе Арджуна Шридхарана не упоминается неопределенное поведение.
Ответы:
Управляющее резюме:
Полный ответ:
Чтобы определить размер вашего массива в байтах, вы можете использовать
sizeof
оператор:На моем компьютере длина целых 4 байта, поэтому n равно 68.
Чтобы определить количество элементов в массиве, мы можем разделить общий размер массива на размер элемента массива. Вы можете сделать это с типом, как это:
и получите правильный ответ (68/4 = 17), но если тип
a
изменится, у вас будет неприятная ошибка, если вы забудете изменитьsizeof(int)
также.Поэтому предпочтительным делителем является
sizeof(a[0])
или эквивалентенsizeof(*a)
размер первого элемента массива.Еще одним преимуществом является то, что теперь вы можете легко параметризовать имя массива в макросе и получить:
источник
ARRAYSIZE
определенный макросWinNT.h
(который добавляется другими заголовками). Таким образом, пользователям WinAPI не нужно определять свои собственные макро.static int a[20];
. Но ваш комментарий полезен для читателей, которые могут не осознавать разницу между массивом и указателем.Это
sizeof
правильный путь, если вы имеете дело с массивами, не полученными в качестве параметров. Массив, отправленный в качестве параметра функции, рассматривается как указатель, поэтомуsizeof
он возвращает размер указателя, а не размер массива.Таким образом, внутри функции этот метод не работает. Вместо этого всегда передавайте дополнительный параметр,
size_t size
указывающий количество элементов в массиве.Тестовое задание:
Вывод (в 64-битной ОС Linux):
Вывод (в 32-битной ОС Windows):
источник
length of parameter:2
если передается только указатель на первый элемент массива?(sizeof array / sizeof *array)
.Стоит отметить, что
sizeof
это не помогает при работе со значением массива, который распался на указатель: даже если он указывает на начало массива, для компилятора он такой же, как указатель на один элемент этого массива , Указатель не «запоминает» что-либо еще о массиве, который использовался для его инициализации.источник
char
32 битами. Все, что говорится в стандарте, заключается в том, что могут быть представлены целочисленные значения от 0 до 127, а его диапазон составляет по крайней мере от -127 до 127 (символ подписан) или от 0 до 255 (символ без знака).Размер «трюка» - лучший способ, который я знаю, с одним небольшим, но (для меня это главная любимая мозоль) важным изменением в использовании скобок.
Как ясно видно из статьи в Википедии, С
sizeof
не является функцией; это оператор . Таким образом, он не требует скобок вокруг своего аргумента, если аргумент не является именем типа. Это легко запомнить, поскольку аргумент выглядит как приведенное выражение, которое также использует круглые скобки.Итак: если у вас есть следующее:
Вы можете найти количество элементов с кодом следующим образом:
Для меня это читается намного проще, чем альтернатива с круглыми скобками. Я также предпочитаю использовать звездочку в правой части раздела, поскольку она более краткая, чем индексирование.
Конечно, это также время компиляции, поэтому нет необходимости беспокоиться о разделении, влияющем на производительность программы. Так что используйте эту форму везде, где можете.
Всегда лучше использовать sizeof на реальном объекте, когда он у вас есть, а не на типе, поскольку тогда вам не нужно беспокоиться о том, чтобы сделать ошибку и указать неверный тип.
Например, скажем, у вас есть функция, которая выводит некоторые данные в виде потока байтов, например, по сети. Давайте вызовем функцию
send()
и заставим ее принимать в качестве аргументов указатель на объект для отправки и количество байтов в объекте. Итак, прототип становится:А затем вам нужно отправить целое число, поэтому вы кодируете его так:
Теперь вы ввели тонкий способ выстрелить себе в ногу, указав тип
foo
в двух местах. Если одно меняется, а другое нет, код ломается. Таким образом, всегда делайте это так:Теперь вы защищены. Конечно, вы дублируете имя переменной, но вероятность того, что компилятор может обнаружить ее, если вы ее измените, высока.
источник
sizeof(int)
меньших инструкций, чемsizeof(foo)
?int x = 1+1;
противint x = (1+1);
. Здесь круглые скобки сугубо просто эстетичны.sizeof
всегда будет постоянным в C ++ и C89. С массивами переменной длины C99 это может быть оценено во время выполнения.sizeof
может быть оператором, но это следует рассматривать как функцию в соответствии с Линусом Торвальдсом. Согласен. Прочитайте его рациональное здесь: lkml.org/lkml/2012/7/11/103Проверьте эту ссылку для объяснения
источник
ptrdiff_t
. (Обычно в 64-битной системе это будет больший тип, чемint
). Даже если вы изменитеint
наptrdiff_t
в этом коде, он все равно имеет ошибку, еслиarr
занимает более половины адресного пространства./3G
возможностью разделения пользователя / ядра 3G / 1G, что позволяет иметь размер массивов до 75% от размера адресного пространства.foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];
качестве глобальных переменных.buf3[]
декларация терпит неудачу, поскольку(&buf1)[1] - buf1
не является константой.Вы можете использовать оператор sizeof, но он не будет работать для функций, поскольку он будет использовать ссылку на указатель. Чтобы найти длину массива, выполните следующие действия:
Код, изначально найденный здесь: C программа для поиска количества элементов в массиве
источник
Если вы знаете тип данных массива, вы можете использовать что-то вроде:
Или, если вы не знаете тип данных массива, вы можете использовать что-то вроде:
Примечание. Это работает, только если массив не определен во время выполнения (например, malloc) и массив не передан в функцию. В обоих случаях
arr
(имя массива) является указателем.источник
int noofele = sizeof(arr)/sizeof(int);
только наполовину лучше, чем кодированиеint noofele = 9;
. Использованиеsizeof(arr)
поддерживает гибкость при изменении размера массива. Все жеsizeof(int)
нуждается в обновлении, если типarr[]
изменения. Лучше использовать,sizeof(arr)/sizeof(arr[0])
даже если тип хорошо известен. Непонятно, почему используетсяint
дляnoofele
противsize_t
, тип, возвращаемыйsizeof()
.Макрос,
ARRAYELEMENTCOUNT(x)
который все используют, оценивает неправильно . На самом деле, это просто деликатный вопрос, потому что у вас не может быть выражений, которые приводят к типу «массив».На самом деле оценивается как:
В то время как
Он правильно оценивает:
Это действительно не имеет непосредственного отношения к размеру массивов. Я только что заметил много ошибок из-за неправильного наблюдения за тем, как работает препроцессор Си. Вы всегда переносите параметр макроса, а не выражение, в которое может быть вовлечено.
Это правильно; мой пример был плохим. Но это именно то, что должно произойти. Как я упоминал ранее
p + 1
, в конечном итоге это будет указатель типа и сделает недействительным весь макрос (как если бы вы пытались использовать макрос в функции с параметром указателя).В конце дня, в данном конкретном случае, ошибка на самом деле не имеет значения (так что я просто трачу время каждого; хазз!), Потому что у вас нет выражений с типом «массив». Но на самом деле вопрос о тонкостях оценки препроцессора, я думаю, является важным.
источник
(sizeof (x) / sizeof (*x))
?Для многомерных массивов это немного сложнее. Часто люди определяют явные макроконстанты, т.е.
Но эти константы могут быть оценены и во время компиляции с помощью sizeof :
Обратите внимание, что этот код работает на C и C ++. Для массивов с более чем двумя измерениями используйте
и т.д. до бесконечности.
источник
источник
array
имеет, вам не нужно использовать ,sizeof(array) / sizeof(array[0])
еслиarray
это массив либоchar
,unsigned char
илиsigned char
- Цитата из C18,6.5.3.4 / 4: «Когда SizeOf применяются к операнду , который имеет тип CHAR, беззнаковый символ или символ подписанный , (или его квалифицированная версия) результат равен 1 ». В этом случае вы можете просто сделать,sizeof(array)
как описано в моем ответе .Размер массива в C:
источник
size_t size_of_element
ещеint
сint n = sizeof (a) / sizeof (a[0]);
и неsize_t n = sizeof (a) / sizeof (a[0]);
char a[INT_MAX + 1u];
,int n
как он используется вint n = sizeof (a) / sizeof (a[0]);
недостаточном (это УБ). Использованиеsize_t n = sizeof (a) / sizeof (a[0]);
не влечет за собой этой проблемы.Я бы посоветовал никогда не использовать
sizeof
(даже если это можно использовать) для получения любого из двух разных размеров массива, либо по количеству элементов, либо в байтах, которые являются двумя последними случаями, которые я здесь показываю. Для каждого из двух размеров макросы, показанные ниже, могут быть использованы для повышения безопасности. Причина заключается в том, чтобы сделать понятным намерение кода для сопровождающих, и отличиеsizeof(ptr)
отsizeof(arr)
на первый взгляд (которое написано таким образом не очевидно), чтобы ошибки были очевидны для всех, кто читает код.TL; DR:
must_be_array(arr)
(определено ниже) IS необходим, так как-Wsizeof-pointer-div
глючит (по состоянию на апрель / 2020):В этой теме были обнаружены важные ошибки: https://lkml.org/lkml/2015/9/3/428.
Я не согласен с решением, которое предлагает Линус, которое никогда не использует нотацию массива для параметров функций.
Мне нравится обозначение массива как документация, что указатель используется в качестве массива. Но это означает, что необходимо применять надежное решение, чтобы невозможно было написать код с ошибками.
Из массива у нас есть три размера, которые мы могли бы знать:
Размер элементов массива
Первый очень прост, и не имеет значения, имеем ли мы дело с массивом или указателем, потому что это делается так же.
Пример использования:
qsort()
нужно это значение в качестве третьего аргумента.Для двух других размеров, которые являются темой вопроса, мы хотим убедиться, что мы имеем дело с массивом, и прервать компиляцию, если нет, потому что, если мы имеем дело с указателем, мы получим неправильные значения , Когда компиляция будет прервана, мы сможем легко увидеть, что мы имеем дело не с массивом, а с указателем, а нам просто нужно написать код с переменной или макросом, который хранит размер массив за указателем.
Количество элементов в массиве
Этот является наиболее распространенным, и многие ответы предоставили вам типичный макрос ARRAY_SIZE:
Учитывая, что результат ARRAY_SIZE обычно используется со знаковыми переменными типа
ptrdiff_t
, полезно определить подписанный вариант этого макроса:Массивы с более чем
PTRDIFF_MAX
одним членом будут давать недопустимые значения для этой подписанной версии макроса, но после чтения C17 :: 6.5.6.9 подобные массивы уже играют с огнем. ТолькоARRAY_SIZE
иsize_t
следует использовать в этих случаях.Последние версии компиляторов, такие как GCC 8, будут предупреждать вас, когда вы применяете этот макрос к указателю, поэтому он безопасен (есть другие способы сделать его более безопасным со старыми компиляторами).
Он работает путем деления размера в байтах всего массива на размер каждого элемента.
Примеры использования:
Если бы эти функции не использовали массивы, а вместо этого получили их в качестве параметров, прежний код не скомпилировался бы, поэтому было бы невозможно иметь ошибку (учитывая, что используется последняя версия компилятора или что используется какой-то другой прием) и нам нужно заменить вызов макроса значением:
Размер в байтах, который массив использует в памяти
ARRAY_SIZE
обычно используется в качестве решения предыдущего случая, но этот случай редко пишется безопасно, возможно потому, что он менее распространен.Обычный способ получить это значение - использовать
sizeof(arr)
. Проблема: та же, что и с предыдущей; если у вас есть указатель вместо массива, ваша программа сойдет с ума.Решение проблемы включает использование того же макроса, что и раньше, который, как мы знаем, безопасен (он нарушает компиляцию, если он применяется к указателю):
Как это работает, очень просто: оно отменяет деление, которое
ARRAY_SIZE
происходит, поэтому после математических отмен вы получите только одинsizeof(arr)
, но с дополнительной безопасностьюARRAY_SIZE
конструкции.Пример использования:
memset()
нужно это значение в качестве третьего аргумента.Как и раньше, если массив получен как параметр (указатель), он не будет компилироваться, и нам придется заменить вызов макроса значением:
Обновление (23 апреля / 2020):
-Wsizeof-pointer-div
глючит :Сегодня я узнал, что новое предупреждение в GCC работает, только если макрос определен в заголовке, который не является системным заголовком. Если вы определяете макрос в заголовке, который установлен в вашей системе (обычно
/usr/local/include/
или/usr/include/
) (#include <foo.h>
), компилятор НЕ выдаст предупреждение (я попробовал GCC 9.3.0).Итак, мы
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
хотим и хотим сделать это безопасным. Нам понадобится C11_Static_assert()
и некоторые расширения GCC: операторы и объявления в выражениях , __builtin_types_compatible_p :Теперь
ARRAY_SIZE()
это абсолютно безопасно, и, следовательно, все его производные будут в безопасности.Обновление: libbsd предоставляет
__arraycount()
:Libbsd предоставляет макрос
__arraycount()
in<sys/cdefs.h>
, что небезопасно, поскольку в нем отсутствует пара круглых скобок, но мы можем сами добавить эти скобки, и поэтому нам даже не нужно записывать деление в нашем заголовке (зачем дублировать уже существующий код? ). Этот макрос определен в системном заголовке, поэтому, если мы его используем, мы вынуждены использовать макросы выше.Некоторые системы обеспечивают
nitems()
в<sys/param.h>
вместо этого, и в некоторых системах обоих. Вы должны проверить свою систему и использовать ту, которая у вас есть, и, возможно, использовать некоторые условные выражения препроцессора для переносимости и поддержки обоих.Обновление: разрешить использование макроса в области видимости файла:
К сожалению,
({})
расширение gcc не может быть использовано на уровне файла. Чтобы иметь возможность использовать макрос в области видимости файла, статическое утверждение должно быть внутриsizeof(struct {})
. Затем умножьте его на,0
чтобы не повлиять на результат. Приведение к(int)
может быть полезно для имитации возвращаемой функции(int)0
(в этом случае это не обязательно, но затем ее можно использовать для других целей).источник
sizeof(arr)
), не отображаются в другом месте:ARRAY_BYTES(arr)
.sizeof
, а использовать вместо этого эти конструкции; если вам хочется писать эти конструкции каждый раз, вы, скорее всего, совершите ошибку (очень часто, если вы копируете вставить, и также очень часто, если вы пишете их каждый раз, потому что у них много скобок) ...sizeof
явно небезопасно (причины в ответе), и не использование макросов, а использование предоставленных мною конструкций, каждый раз, еще более небезопасно, поэтому единственный путь это макросы.«Вы ввели тонкий способ выстрелить себе в ногу»
C 'родные' массивы не сохраняют свой размер. Поэтому рекомендуется сохранять длину массива в отдельной переменной / const и передавать ее всякий раз, когда вы передаете массив, то есть:
Вы ДОЛЖНЫ всегда избегать нативных массивов (если вы не можете, в этом случае, следите за своей ногой). Если вы пишете на C ++, используйте контейнер STL 'vector'. «По сравнению с массивами они обеспечивают почти одинаковую производительность», и они гораздо полезнее!
Смотрите: http://www.cplusplus.com/reference/stl/vector/
источник
enum
объявление.источник
Если вы действительно хотите сделать это, чтобы обойти массив, я предлагаю реализовать структуру для хранения указателя на тип, для которого вы хотите массив, и целого числа, представляющего размер массива. Затем вы можете передать это своим функциям. Просто назначьте этому указателю значение переменной массива (указатель на первый элемент). Затем вы можете перейти
Array.arr[i]
к получению i-го элемента и использоватьArray.size
для получения количества элементов в массиве.Я включил некоторый код для вас. Это не очень полезно, но вы можете расширить его с помощью дополнительных функций. Если честно, если вы хотите именно этого, вам следует прекратить использовать C и использовать другой язык со встроенными функциями.
источник
strcpy(d.name, name);
без обработки переполнения.Лучше всего сохранить эту информацию, например, в структуре:
Реализуйте все необходимые функции, такие как создание, уничтожение, проверка равенства и все остальное, что вам нужно. Проще передать в качестве параметра.
источник
int elements
противsize_t elements
?Функция
sizeof
возвращает количество байтов, которое используется вашим массивом в памяти. Если вы хотите вычислить количество элементов в вашем массиве, вы должны разделить это число наsizeof
тип переменной массива. Допустимint array[10];
, если на вашем компьютере переменная типа integer 32-битная (или 4-байтовая), для того, чтобы получить размер вашего массива, вы должны сделать следующее:источник
Вы можете использовать
&
оператор. Вот исходный код:Вот пример вывода
источник
ptrdiff_t
.sizeof()
результаты вsize_t
. С не определяет, кто шире или выше / того же ранга. Таким образом, тип фактора((char *)(&a+1)-(char *)a)/(sizeof(a[0]))
не обязательноsize_t
и, следовательно, печать сz
может привести к UB. Простого использованияprintf("The size of array a is %zu\n", sizeof a/sizeof a[0]);
достаточно.(char *)(&a+1)-(char *)a
не является константой и может быть рассчитана во время выполнения, даже с фиксированным размеромa[10]
.sizeof(a)/sizeof(a[0])
в этом случае выполняется постоянно во время компиляции.Самый простой ответ:
источник
Более элегантное решение будет
источник
Помимо уже предоставленных ответов, я хочу указать на особый случай с использованием
Если
a
это либо массивchar
,unsigned char
либоsigned char
вам не нужно использоватьsizeof
дважды, так какsizeof
выражение с одним операндом этих типов всегда приводит к1
.Цитата из C18,6.5.3.4 / 4:
Таким образом,
sizeof(a) / sizeof (a[0])
было бы эквивалентно,NUMBER OF ARRAY ELEMENTS / 1
еслиa
это массив типаchar
,unsigned char
илиsigned char
. Деление на 1 является излишним.В этом случае вы можете просто сократить и сделать:
Например:
Если вы хотите доказательства, вот ссылка на GodBolt .
Тем не менее, подразделение поддерживает безопасность, если тип значительно изменяется (хотя эти случаи редки).
источник
Примечание: это может дать вам неопределенное поведение, как указано ММ в комментарии.
Для более подробной информации смотрите здесь, а также здесь .
источник
*
оператор не может быть применен к пришедшим к конечному указателю*(&a+1) - a;
, отличается от(&a)[1] - a;
выше, не так*(&a+1)
и(&a)[1]
считаются 1 в конце прошлого?x[y]
определяется как*(x + (y))