Як визначити розмір масиву в C?
Тобто кількість елементів, які може містити масив?
Як визначити розмір масиву в C?
Тобто кількість елементів, які може містити масив?
Відповіді:
Резюме:
int a[17];
size_t n = sizeof(a)/sizeof(a[0]);
Повна відповідь:
Щоб визначити розмір масиву в байтах, ви можете скористатися sizeof
оператором:
int a[17];
size_t n = sizeof(a);
На моєму комп’ютері ints - 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);
ARRAYSIZE
макро, визначений у WinNT.h
(який потрапляє в інші заголовки). Таким чином, користувачам WinAPI не потрібно визначати власну макро.
static int a[20];
. Але ваш коментар корисний читачам, які можуть не усвідомити різницю між масивом і вказівником.
Це 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[])
{
int array[] = { 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: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2
Вихід (в 32-бітній ОС Windows):
sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
length of parameter:2
якщо передано лише вказівник на елемент 1-го масиву?
(sizeof array / sizeof *array)
.
Варто зауважити, що sizeof
не допомагає мати справу зі значенням масиву, який занепав до покажчика: навіть якщо він вказує на початок масиву, для компілятора це те саме, що вказівник на один елемент цього масиву . Вказівник нічого не «пам’ятає» про масив, який використовувався для його ініціалізації.
int a[10];
int* p = a;
assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));
char
32 бітами. Все стандартне говорить про те, що цілі значення від 0 до 127 можуть бути представлені, а його діапазон принаймні або від -127 до 127 (знак підписано), або від 0 до 255 (знак не підписаний).
Розмір "трюку" - це найкращий спосіб, який я знаю, з однією невеликою, але (на мене, це головним вихованцем) важливою зміною використання дужок.
Як зрозуміло запис у Вікіпедії, C - sizeof
це не функція; це оператор . Таким чином, він не вимагає дужок навколо свого аргументу, якщо аргумент не є назвою типу. Це легко запам’ятати, оскільки він робить аргумент схожим на виразний вираз, який також використовує дужки.
Отже: Якщо у вас є наступне:
int myArray[10];
Ви можете знайти кількість елементів з таким кодом:
size_t n = sizeof myArray / sizeof *myArray;
Це, як на мене, читається набагато простіше, ніж альтернатива з дужками. Я також віддаю перевагу використанню зірочки в правій частині підрозділу, оскільки це швидше, ніж індексація.
Звичайно, це теж час компіляції, тому не потрібно турбуватися про поділ, що впливає на ефективність програми. Тому використовуйте цю форму де завгодно.
Завжди найкраще використовувати sizeof на фактичному об’єкті, коли у вас його є, а не для типу, оскільки тоді вам не потрібно турбуватися про помилку та констатацію неправильного типу.
Наприклад, скажімо, у вас є функція, яка виводить деякі дані у вигляді потоку байтів, наприклад, через мережу. Давайте викличемо функцію send()
і зробимо її в якості аргументів вказівник на об'єкт, що надсилається, та кількість байтів в об'єкті. Отже, прототипом стає:
void send(const void *object, size_t size);
І тоді вам потрібно надіслати ціле число, щоб ви кодували його так:
int foo = 4711;
send(&foo, sizeof (int));
Тепер ви представили тонкий спосіб стріляти в ногу, вказавши тип foo
в двох місцях. Якщо одна зміна, а інша ні, код порушується. Таким чином, завжди робіть це так:
send(&foo, sizeof 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
int size = (&arr)[1] - arr;
Перевірте це посилання для пояснення
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[]
декларація failes як (&buf1)[1] - buf1
не є постійною.
Ви можете використовувати оператор sizeof, але він не буде працювати для функцій, оскільки він буде приймати посилання на покажчик, ви можете зробити наступне, щоб знайти довжину масиву:
len = sizeof(arr)/sizeof(arr[0])
Код, спочатку знайдений тут: програма C для пошуку кількості елементів у масиві
Якщо ви знаєте тип даних масиву, ви можете використовувати щось на зразок:
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
vs. size_t
, тип, що повертається sizeof()
.
Макрос, ARRAYELEMENTCOUNT(x)
який всі використовують, оцінює неправильно . Це, реально, є лише чутливою справою, тому що ви не можете мати вирази, що призводять до типу "масив".
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))
ARRAYELEMENTCOUNT(p + 1);
Фактично оцінюється як:
(sizeof (p + 1) / sizeof (p + 1[0]));
Тоді як
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])
ARRAYELEMENTCOUNT(p + 1);
Він правильно оцінює:
(sizeof (p + 1) / sizeof (p + 1)[0]);
Це дійсно не має великого відношення до розміру масивів явно. Я щойно помітив чимало помилок, коли насправді не спостерігав, як працює препроцесор C. Ви завжди обгортаєте параметр макросу, а не може бути залучений вираз у.
Це вірно; мій приклад був поганий. Але саме це має відбутися. Як я вже згадував, ви p + 1
отримаєте тип вказівника і визнаєте недійсним весь макрос (як якщо б ви намагалися використовувати макрос у функції з параметром вказівника).
Зрештою, у цьому конкретному випадку помилка насправді не має значення (тому я просто витрачаю час усім; huzzah!), Тому що у вас немає виразів з типом "масиву". Але насправді сенс щодо підпроцесів оцінки препроцесорів, я думаю, є важливим.
(sizeof (x) / sizeof (*x))
?
Для багатовимірних масивів це набагато складніше. Часто люди визначають явні макроконстанти, тобто
#define g_rgDialogRows 2
#define g_rgDialogCols 7
static char const* g_rgDialog[g_rgDialogRows][g_rgDialogCols] =
{
{ " ", " ", " ", " 494", " 210", " Generic Sample Dialog", " " },
{ " 1", " 330", " 174", " 88", " ", " OK", " " },
};
Але ці константи можна оцінити і в час компіляції за розміромofof :
#define rows_of_array(name) \
(sizeof(name ) / sizeof(name[0][0]) / columns_of_array(name))
#define columns_of_array(name) \
(sizeof(name[0]) / sizeof(name[0][0]))
static char* g_rgDialog[][7] = { /* ... */ };
assert( rows_of_array(g_rgDialog) == 2);
assert(columns_of_array(g_rgDialog) == 7);
Зауважте, що цей код працює в C і C ++. Для масивів, які використовують більше двох розмірів
sizeof(name[0][0][0])
sizeof(name[0][0][0][0])
тощо, ad infinitum.
sizeof(array) / sizeof(array[0])
array
має, вам не потрібно використовувати, sizeof(array) / sizeof(array[0])
якщо array
це масив будь-якого char
, unsigned char
або signed char
- Цитата від C18,6.5.3.4 / 4: "Коли sizeof застосовується до операнду, який має тип char, неподписаний char або підписаний char , (або його кваліфікована версія) результат дорівнює 1. " У цьому випадку ви можете просто зробити так, sizeof(array)
як пояснено в моїй спеціальній відповіді .
Розмір масиву в C:
int a[10];
size_t size_of_array = sizeof(a); // Size of array a
int n = sizeof (a) / sizeof (a[0]); // Number of elements in array a
size_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]);
char a[INT_MAX + 1u];
, int n
як використовується в int n = sizeof (a) / sizeof (a[0]);
недостатній (це UB). Використання size_t n = sizeof (a) / sizeof (a[0]);
не спричиняє цієї проблеми.
Я б радив ніколи не використовувати sizeof
(навіть якщо це можна використовувати), щоб отримати будь-який з двох різних розмірів масиву, або за кількістю елементів, або в байтах, які я показую два останніх випадки. Для кожного з двох розмірів макроси, показані нижче, можна використовувати, щоб зробити це безпечнішим. Причина полягає в тому, щоб зробити очевидним намір коду для обслуговуючого персоналу, і відмінність sizeof(ptr)
від sizeof(arr)
першого погляду (який написано таким чином не очевидно), так що помилки потім очевидні для всіх, хто читає код.
TL; DR:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + must_be_array(arr))
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
must_be_array(arr)
(визначено нижче) ІС потрібен, як -Wsizeof-pointer-div
і баггі (станом на квітень / 2020):
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a) ( \
0 * (int)sizeof( \
struct { \
Static_assert_array(a); \
char ISO_C_forbids_a_struct_with_no_members__; \
} \
) \
)
У цій темі були важливі помилки: https://lkml.org/lkml/2015/9/3/428
Я не погоджуюся з рішенням, яке пропонує Лінус, і це ніколи не використовувати позначення масиву для параметрів функцій.
Мені подобається позначення масиву як документація про те, що вказівник використовується як масив. Але це означає, що потрібно застосувати нерозумне рішення, щоб неможливо було написати помилку.
З масиву у нас є три розміри, про які ми могли б знати:
Перший дуже простий, і не має значення, чи маємо ми справу з масивом або вказівником, оскільки це робиться так само.
Приклад використання:
void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
qsort(arr, nmemb, sizeof(arr[0]), cmp);
}
qsort()
потребує цього значення як свого третього аргументу.
Для інших двох розмірів, які є темою питання, ми хочемо переконатися, що ми маємо справу з масивом, і розірвемо компіляцію, якщо ні, тому що якщо ми маємо справу з вказівником, ми отримаємо неправильні значення . Коли компіляція порушена, ми зможемо легко побачити, що ми мали справу не з масивом, а з вказівником, і нам просто доведеться записати код зі змінною або макросом, який зберігає розмір масив за вказівником.
Цей найпоширеніший, і багато відповідей дали вам типовий макрос ARRAY_SIZE:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
З огляду на те, що результат ARRAY_SIZE зазвичай використовується з підписаними змінними типу ptrdiff_t
, добре визначити підписаний варіант цього макросу:
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
Масиви з більш ніж 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)
. Проблема: така ж, як і у попереднього; якщо у вас є вказівник замість масиву, ваша програма втратить гайки.
Рішення проблеми передбачає використання того самого макросу, що і раніше, який ми знаємо як безпечний (він порушує компіляцію, якщо застосовано до покажчика):
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
Принцип роботи дуже простий: він скасовує поділ, який ARRAY_SIZE
робить, тож після математичних скасувань ви закінчуєтесь лише одним sizeof(arr)
, але з додатковою безпекоюARRAY_SIZE
конструкції.
Приклад використання:
void foo(ptrdiff_t nmemb)
{
int arr[nmemb];
memset(arr, 0, ARRAY_BYTES(arr));
}
memset()
потребує цього значення як свого третього аргументу.
Як і раніше, якщо масив отриманий як параметр (вказівник), він не компілюється, і нам доведеться замінити виклик макросу на значення:
void foo(ptrdiff_t nmemb, int arr[nmemb])
{
memset(arr, 0, sizeof(arr[0]) * nmemb);
}
-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 :
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_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()
це повністю безпечно, а тому всі його похідні будуть безпечними.
__arraycount()
:Libbsd забезпечує макрос __arraycount()
в <sys/cdefs.h>
, який є небезпечним , так як йому не вистачає пару дужок, але ми можемо додати ці круглі дужки себе, і тому ми навіть не потрібно писати поділ в нашому заголовку (чому б нам дублювати код , який вже існує? ). Цей макрос визначений у заголовку системи, тому якщо ми його використовуємо, ми змушені використовувати макроси вище.
#include <sys/cdefs.h>
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define ARRAY_SIZE(arr) ( \
{ \
Static_assert_array(arr); \
__arraycount((arr)); \
} \
)
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
Деякі системи забезпечують nitems()
в <sys/param.h>
замість цього, і в деяких системах обох. Ви повинні перевірити свою систему та використовувати ту, яка є у вас, а можливо, використовувати деякі умови попереднього процесу для переносу та підтримки обох.
На жаль, ({})
розширення gcc не можна використовувати в області файлів. Щоб мати можливість використовувати макрос в області файлу, статичне твердження повинно бути всередині sizeof(struct {})
. Потім помножте його на, 0
щоб не вплинути на результат. Заголовок (int)
може бути корисним для імітації функції, яка повертається (int)0
(у цьому випадку це не потрібно, але тоді він може бути використаний повторно для інших речей).
#include <sys/cdefs.h>
#define is_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a) (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a) _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a) ( \
0 * (int)sizeof( \
struct { \
Static_assert_array(a); \
char ISO_C_forbids_a_struct_with_no_members__; \
} \
) \
)
#define ARRAY_SIZE(arr) (__arraycount((arr)) + must_be_array(arr))
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
sizeof(arr)
), не відображаються в іншому місці: ARRAY_BYTES(arr)
.
sizeof
, а використовувати цю конструкцію замість цього; якщо ви хочете писати ці конструкції кожен раз, ви, ймовірно, помилитесь (дуже часто, якщо ви копіюєте пасти, а також дуже часто, якщо ви пишете їх кожен раз, оскільки у них багато дужок) ...
sizeof
явно небезпечно (причини є у відповіді), і не використовуючи макроси, а використовуючи конструкції, які я надав, щоразу ще небезпечніше, тому єдиний шлях це макроси.
"ви ввели тонкий спосіб стріляти себе в ногу"
C "рідні" масиви не зберігають їх розміри. Тому рекомендується зберігати довжину масиву в окремій змінній / const і передавати її щоразу, коли ви передаєте масив, тобто:
#define MY_ARRAY_LENGTH 15
int myArray[MY_ARRAY_LENGTH];
Ви завжди повинні уникати рідних масивів (якщо ви не можете, в такому випадку, пам'ятайте про ступні). Якщо ви пишете C ++, використовуйте контейнер STL 'вектор'. "У порівнянні з масивами вони забезпечують майже однакову продуктивність", і вони набагато корисніші!
// vector is a template, the <int> means it is a vector of ints
vector<int> numbers;
// push_back() puts a new value at the end (or back) of the vector
for (int i = 0; i < 10; i++)
numbers.push_back(i);
// Determine the size of the array
cout << numbers.size();
enum
декларації.
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
Якщо ви дійсно хочете зробити це, щоб пройти навколо свого масиву, я пропоную реалізувати структуру для збереження вказівника на тип, для якого ви хочете масив, і ціле число, що представляє розмір масиву. Тоді ви можете передати це навколо своїх функцій. Просто призначте значення змінної масиву (вказівник на перший елемент) цьому вказівнику. Потім ви можете перейти 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
*/
typedef struct MyType
{
int age;
char name[20];
} MyType;
typedef struct MyTypeArray
{
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. */
MyTypeArray array = new_MyTypeArray(2, arr);
/* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
print_MyType_names(array);
return 0;
}
strcpy(d.name, name);
справляється із переповненням.
Найкращий спосіб - це зберегти цю інформацію, наприклад, у структурі:
typedef struct {
int *array;
int elements;
} list_s;
Реалізуйте всі необхідні функції, такі як створення, руйнування, перевірка рівності та все інше, що вам потрібно. Легше пройти як параметр.
int elements
проти size_t elements
?
Функція sizeof
повертає кількість байтів, які використовує ваш масив у пам'яті. Якщо ви хочете обчислити кількість елементів у своєму масиві, слід розділити це число на sizeof
змінний тип масиву. Скажімо int array[10];
, якщо ціле число змінної типу на вашому комп’ютері становить 32 біти (або 4 байти), щоб отримати розмір масиву, слід зробити наступне:
int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);
Ви можете скористатися &
оператором. Ось вихідний код:
#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])));
return 0;
};
Ось зразок виводу
1549216672
1549216712
---- diff----
4
The size of array a is 10
ptrdiff_t
. sizeof()
результати в size_t
. C не визначає, який ширший чи вищий / той самий ранг. Тож тип коефіцієнта ((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])
в цьому випадку константа робиться під час компіляції.
Більш елегантним рішенням буде
size_t size = sizeof(a) / sizeof(*a);
Окрім вже наданих відповідей, я хочу зазначити окремий випадок використання
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 .
Тим не менш, відділ підтримує безпеку, якщо тип суттєво змінюється (хоча ці випадки рідкісні).
Примітка. Це може надати вам не визначену поведінку, як наголошує М.М. у коментарі.
int a[10];
int size = (*(&a+1)-a) ;
*
оператор не може бути застосований до людей до кінцевого вказівником
*(&a+1) - a;
, відрізняється від (&a)[1] - a;
вище, чи не обоє, *(&a+1)
і (&a)[1]
рахувати як один минулий кінець?
x[y]
визначається як*(x + (y))