Jak określić rozmiar mojej tablicy w C?
To znaczy, ile elementów może pomieścić tablica?
Jak określić rozmiar mojej tablicy w C?
To znaczy, ile elementów może pomieścić tablica?
Odpowiedzi:
Streszczenie:
int a[17];
size_t n = sizeof(a)/sizeof(a[0]);
Pełna odpowiedź:
Aby określić rozmiar tablicy w bajtach, możesz użyć sizeof
operatora:
int a[17];
size_t n = sizeof(a);
Na moim komputerze ints mają 4 bajty, więc n ma 68.
Aby określić liczbę elementów w tablicy, możemy podzielić całkowity rozmiar tablicy przez rozmiar elementu tablicy. Możesz to zrobić z tym typem:
int a[17];
size_t n = sizeof(a) / sizeof(int);
i uzyskać prawidłową odpowiedź (68/4 = 17), ale jeśli a
zmienił się typ
, miałbyś paskudny błąd, gdybyś również zapomniał zmienić sizeof(int)
.
Tak więc preferowanym dzielnikiem jest sizeof(a[0])
lub równoważny sizeof(*a)
, rozmiar pierwszego elementu tablicy.
int a[17];
size_t n = sizeof(a) / sizeof(a[0]);
Kolejną zaletą jest to, że możesz teraz łatwo sparametryzować nazwę tablicy w makrze i uzyskać:
#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
int a[17];
size_t n = NELEMS(a);
ARRAYSIZE
makro zdefiniowane w WinNT.h
(które jest pobierane przez inne nagłówki). Dlatego użytkownicy WinAPI nie muszą definiować własnego makra.
static int a[20];
. Ale twój komentarz jest przydatny dla czytelników, którzy mogą nie zdawać sobie sprawy z różnicy między tablicą a wskaźnikiem.
sizeof
Droga jest właściwa droga IFF masz do czynienia z tablicami nie odebranych jako parametry. Tablica wysłana jako parametr do funkcji jest traktowana jako wskaźnik, więc sizeof
zwróci rozmiar wskaźnika, zamiast rozmiaru tablicy.
Dlatego w funkcjach wewnętrznych ta metoda nie działa. Zamiast tego zawsze przekazuj dodatkowy parametr size_t size
wskazujący liczbę elementów w tablicy.
Test:
#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]) ));
}
Dane wyjściowe (w 64-bitowym systemie operacyjnym Linux):
sizeof of array: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2
Dane wyjściowe (w 32-bitowym systemie operacyjnym Windows):
sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
length of parameter:2
jeśli przekazywany jest tylko wskaźnik do pierwszego elementu tablicy?
(sizeof array / sizeof *array)
.
Warto zauważyć, że sizeof
nie pomaga to w przypadku wartości tablicy, która uległa rozkładowi na wskaźnik: mimo że wskazuje na początek tablicy, w przypadku kompilatora jest ona taka sama jak wskaźnik do pojedynczego elementu tej tablicy . Wskaźnik nie „pamięta” niczego innego o tablicy, która została użyta do jego zainicjowania.
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 bitami. Wszystko, co mówi standard, to to, że mogą być reprezentowane wartości całkowite od 0 do 127, a jego zakres wynosi co najmniej -127 do 127 (znak jest podpisany) lub 0 do 255 (znak jest niepodpisany).
Rozmiar „sztuczki” to najlepszy sposób, jaki znam, z jedną małą, ale (dla mnie, to jest główną wkurzaniem) istotną zmianą w stosowaniu nawiasów.
Jak wyjaśnia wpis w Wikipedii, C sizeof
nie jest funkcją; to operator . Dlatego nie wymaga nawiasów wokół argumentu, chyba że argument jest nazwą typu. Łatwo to zapamiętać, ponieważ sprawia, że argument wygląda jak rzutowane wyrażenie, które również używa nawiasów.
Więc: jeśli masz następujące elementy:
int myArray[10];
Możesz znaleźć liczbę elementów o takim kodzie:
size_t n = sizeof myArray / sizeof *myArray;
To dla mnie brzmi o wiele łatwiej niż alternatywa z nawiasami. Popieram także użycie gwiazdki w prawej części działu, ponieważ jest bardziej zwięzła niż indeksowanie.
Oczywiście, to także czas kompilacji, więc nie musisz się martwić podziałem wpływającym na wydajność programu. Więc skorzystaj z tego formularza, gdziekolwiek możesz.
Zawsze najlepiej jest używać sizeof na rzeczywistym obiekcie, gdy go masz, a nie na typie, ponieważ wtedy nie musisz się martwić o popełnienie błędu i podanie niewłaściwego typu.
Załóżmy na przykład, że masz funkcję, która wyprowadza niektóre dane jako strumień bajtów, na przykład przez sieć. Wywołajmy funkcję send()
i sprawmy, aby jako argumenty przyjmowała wskaźnik do obiektu do wysłania oraz liczbę bajtów w obiekcie. Prototyp staje się:
void send(const void *object, size_t size);
A potem musisz wysłać liczbę całkowitą, więc kodujesz to w ten sposób:
int foo = 4711;
send(&foo, sizeof (int));
Teraz wprowadziłeś subtelny sposób strzelania sobie w stopę, określając typ foo
w dwóch miejscach. Jeśli jeden się zmieni, a drugi nie, kod się zepsuje. Dlatego zawsze rób tak:
send(&foo, sizeof foo);
Teraz jesteś chroniony. Jasne, powielasz nazwę zmiennej, ale ma duże prawdopodobieństwo uszkodzenia w sposób, który kompilator może wykryć, jeśli ją zmienisz.
sizeof(int)
wymaga mniej instrukcji niż sizeof(foo)
?
int x = 1+1;
kontra int x = (1+1);
. Tutaj nawiasy są absolutnie wyłącznie estetyczne.
sizeof
zawsze będzie stały w C ++ i C89. W przypadku tablic o zmiennej długości C99 można go oceniać w czasie wykonywania.
sizeof
może być operatorem, ale zgodnie z Linusem Torvaldsem należy go traktować jako funkcję. Zgadzam się. Przeczytaj jego uzasadnienie tutaj: lkml.org/lkml/2012/7/11/103
int size = (&arr)[1] - arr;
Sprawdź ten link do wyjaśnienia
ptrdiff_t
. (Zwykle w systemie 64-bitowym będzie to większy typ int
). Nawet jeśli zmienisz int
na ptrdiff_t
w tym kodzie, nadal zawiera błąd, jeśli arr
zajmuje więcej niż połowę przestrzeni adresowej.
/3G
opcją masz podział użytkownika / jądra 3G / 1G, co pozwala mieć rozmiar tablic do 75% wielkości przestrzeni adresowej.
foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];
za zmienne globalne. buf3[]
deklaracja kończy się niepowodzeniem, ponieważ (&buf1)[1] - buf1
nie jest stała.
Możesz użyć operatora sizeof, ale nie będzie on działał dla funkcji, ponieważ weźmie odwołanie do wskaźnika, możesz wykonać następujące czynności, aby znaleźć długość tablicy:
len = sizeof(arr)/sizeof(arr[0])
Pierwotnie znaleziony kod: program C do znalezienia liczby elementów w tablicy
Jeśli znasz typ danych tablicy, możesz użyć czegoś takiego:
int arr[] = {23, 12, 423, 43, 21, 43, 65, 76, 22};
int noofele = sizeof(arr)/sizeof(int);
Lub jeśli nie znasz typu danych tablicy, możesz użyć czegoś takiego:
noofele = sizeof(arr)/sizeof(arr[0]);
Uwaga: Ta funkcja działa tylko wtedy, gdy tablica nie jest zdefiniowana w czasie wykonywania (np. Malloc), a tablica nie jest przekazywana w funkcji. W obu przypadkach arr
(nazwa tablicy) jest wskaźnikiem.
int noofele = sizeof(arr)/sizeof(int);
jest tylko w połowie lepszy niż kodowanie int noofele = 9;
. Użycie sizeof(arr)
zachowuje elastyczność w przypadku zmiany rozmiaru tablicy. Jednak sizeof(int)
wymaga aktualizacji, jeśli rodzaj arr[]
zmiany. Lepszy w użyciu, sizeof(arr)/sizeof(arr[0])
nawet jeśli typ jest dobrze znany. Niejasne, dlaczego int
dla for noofele
vs. size_t
, typ zwracany przez sizeof()
.
Makro, z ARRAYELEMENTCOUNT(x)
którego wszyscy korzystają, ocenia niepoprawnie . W rzeczywistości jest to tylko kwestia drażliwa, ponieważ nie można mieć wyrażeń, które skutkowałyby typem „tablicy”.
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))
ARRAYELEMENTCOUNT(p + 1);
Faktycznie ocenia się jako:
(sizeof (p + 1) / sizeof (p + 1[0]));
Natomiast
/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])
ARRAYELEMENTCOUNT(p + 1);
Poprawnie ocenia:
(sizeof (p + 1) / sizeof (p + 1)[0]);
To naprawdę nie ma wiele wspólnego z rozmiarem tablic. Właśnie zauważyłem wiele błędów wynikających z nieobserwowania, jak działa preprocesor C. Zawsze zawijasz parametr makra, w którym nie może być zaangażowane żadne wyrażenie.
To jest poprawne; mój przykład był zły. Ale tak właśnie powinno być. Jak już wspomniałem p + 1
, skończy jako typ wskaźnika i unieważni całe makro (podobnie jak w przypadku próby użycia makra w funkcji z parametrem wskaźnika).
Na koniec dnia, w tym konkretnym przypadku, błąd tak naprawdę nie ma znaczenia (więc po prostu marnuję czas wszystkich; huzzah!), Ponieważ nie masz wyrażeń z rodzajem „tablicy”. Ale tak naprawdę kwestia subtelności oceny preprocesora uważam za ważną.
(sizeof (x) / sizeof (*x))
?
W przypadku tablic wielowymiarowych jest to nieco bardziej skomplikowane. Często ludzie definiują wyraźne stałe makr, tj
#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", " " },
};
Ale te stałe można również ocenić w czasie kompilacji za pomocą sizeof :
#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);
Zauważ, że ten kod działa w C i C ++. Do tablic o więcej niż dwóch wymiarach użyj
sizeof(name[0][0][0])
sizeof(name[0][0][0][0])
itp., ad infinitum.
sizeof(array) / sizeof(array[0])
array
ma, nie musisz używać, sizeof(array) / sizeof(array[0])
jeśli array
jest tablicą albo char
, unsigned char
albo signed char
- Cytat z C18,6.5.3.4 / 4: „Gdy sizeof jest stosowane do operandu, który ma typ char, unsigned char lub podpisany char , (lub jego kwalifikowana wersja) wynik to 1. ” W takim przypadku możesz po prostu zrobić, sizeof(array)
jak wyjaśniono w mojej dedykowanej odpowiedzi .
Rozmiar tablicy w 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
jeszcze int
z int n = sizeof (a) / sizeof (a[0]);
niesize_t n = sizeof (a) / sizeof (a[0]);
char a[INT_MAX + 1u];
, int n
jak użyte w int n = sizeof (a) / sizeof (a[0]);
jest niewystarczające (jest to UB). Korzystanie size_t n = sizeof (a) / sizeof (a[0]);
nie powoduje tego problemu.
Radziłbym nigdy nie używać sizeof
(nawet jeśli można go użyć), aby uzyskać dowolny z dwóch różnych rozmiarów tablicy, albo w liczbie elementów, albo w bajtach, które są dwoma ostatnimi przypadkami, które tutaj pokazuję. Dla każdego z dwóch rozmiarów można użyć makr pokazanych poniżej, aby zwiększyć bezpieczeństwo. Powodem jest ujawnienie opiekunom intencji kodu i odróżnienie go sizeof(ptr)
od sizeof(arr)
pierwszego spojrzenia (które napisane w ten sposób nie jest oczywiste), aby błędy były widoczne dla wszystkich czytających kod.
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)
(zdefiniowane poniżej) JEST potrzebne, ponieważ -Wsizeof-pointer-div
jest wadliwe (na kwiecień / 2020 r.):
#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__; \
} \
) \
)
Pojawiły się ważne błędy dotyczące tego tematu: https://lkml.org/lkml/2015/9/3/428
Nie zgadzam się z rozwiązaniem, które zapewnia Linus, którym jest nigdy nie używać notacji tablicowej do parametrów funkcji.
Lubię notację tablicową jako dokumentację, że wskaźnik jest używany jako tablica. Oznacza to jednak, że należy zastosować rozwiązanie niezawodne, aby nie można było napisać błędnego kodu.
Z tablicy mamy trzy rozmiary, które chcielibyśmy poznać:
Pierwszy jest bardzo prosty i nie ma znaczenia, czy mamy do czynienia z tablicą czy wskaźnikiem, ponieważ robi się to w ten sam sposób.
Przykład użycia:
void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
qsort(arr, nmemb, sizeof(arr[0]), cmp);
}
qsort()
potrzebuje tej wartości jako trzeciego argumentu.
W przypadku pozostałych dwóch rozmiarów, które są tematem pytania, chcemy się upewnić, że mamy do czynienia z tablicą, a jeśli nie, zepsujemy kompilację, ponieważ jeśli mamy do czynienia ze wskaźnikiem, otrzymamy nieprawidłowe wartości . Gdy kompilacja zostanie zerwana, będziemy mogli łatwo zobaczyć, że nie mamy do czynienia z tablicą, ale ze wskaźnikiem, i będziemy musieli po prostu napisać kod ze zmienną lub makrem, który przechowuje rozmiar tablica za wskaźnikiem.
Ten jest najczęstszy i wiele odpowiedzi dostarczyło typowego makra ARRAY_SIZE:
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
Biorąc pod uwagę, że wynik ARRAY_SIZE jest powszechnie używany z podpisanymi zmiennymi typu ptrdiff_t
, dobrze jest zdefiniować podpisany wariant tego makra:
#define ARRAY_SSIZE(arr) ((ptrdiff_t)ARRAY_SIZE(arr))
Tablice z więcej niż PTRDIFF_MAX
członkami podadzą niepoprawne wartości dla tej podpisanej wersji makra, ale od odczytu C17 :: 6.5.6.9, takie tablice już się bawią ogniem. Tylko ARRAY_SIZE
i size_t
powinien być stosowany w tych przypadkach.
Najnowsze wersje kompilatorów, takie jak GCC 8, ostrzeżą cię, gdy zastosujesz to makro do wskaźnika, więc jest to bezpieczne (istnieją inne metody, aby zapewnić bezpieczeństwo w starszych kompilatorach).
Działa, dzieląc rozmiar w bajtach całej tablicy przez rozmiar każdego elementu.
Przykłady użycia:
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;
}
Gdyby te funkcje nie używały tablic, ale zamiast tego otrzymały je jako parametry, poprzedni kod nie skompilowałby się, więc nie byłoby możliwe wystąpienie błędu (biorąc pod uwagę, że użyto najnowszej wersji kompilatora lub zastosowano inną sztuczkę) , i musimy zastąpić wywołanie makra wartością:
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
jest powszechnie stosowany jako rozwiązanie poprzedniej sprawy, ale ta sprawa rzadko jest pisana bezpiecznie, może dlatego, że jest mniej powszechna.
Typowym sposobem uzyskania tej wartości jest użycie sizeof(arr)
. Problem: taki sam jak w poprzednim; jeśli zamiast tablicy masz wskaźnik, twój program zwariuje.
Rozwiązanie problemu polega na użyciu tego samego makra, co wcześniej, co wiemy, że jest bezpieczne (przerywa kompilację, jeśli zostanie zastosowane do wskaźnika):
#define ARRAY_BYTES(arr) (sizeof((arr)[0]) * ARRAY_SIZE(arr))
Jak to działa, jest bardzo proste: cofa podział, który ARRAY_SIZE
działa, więc po anulowaniu matematyki otrzymujesz tylko jeden sizeof(arr)
, ale z dodatkowym bezpieczeństwem ARRAY_SIZE
konstrukcji.
Przykład użycia:
void foo(ptrdiff_t nmemb)
{
int arr[nmemb];
memset(arr, 0, ARRAY_BYTES(arr));
}
memset()
potrzebuje tej wartości jako trzeciego argumentu.
Tak jak poprzednio, jeśli tablica zostanie odebrana jako parametr (wskaźnik), nie zostanie skompilowana i będziemy musieli zastąpić wywołanie makra wartością:
void foo(ptrdiff_t nmemb, int arr[nmemb])
{
memset(arr, 0, sizeof(arr[0]) * nmemb);
}
-Wsizeof-pointer-div
jest wadliwa :Dzisiaj dowiedziałem się, że nowe ostrzeżenie w GCC działa tylko wtedy, gdy makro jest zdefiniowane w nagłówku, który nie jest nagłówkiem systemowym. Jeśli zdefiniujesz makro w nagłówku, który jest zainstalowany w twoim systemie (zwykle /usr/local/include/
lub /usr/include/
) ( #include <foo.h>
), kompilator NIE wyśle ostrzeżenia (próbowałem GCC 9.3.0).
Więc mamy #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
i chcemy zapewnić bezpieczeństwo. Będziemy potrzebować C11 _Static_assert()
i niektórych rozszerzeń GCC: Instrukcje i deklaracje w wyrażeniach , __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]); \
} \
)
Teraz ARRAY_SIZE()
jest całkowicie bezpieczny, a zatem wszystkie jego pochodne będą bezpieczne.
__arraycount()
:Libbsd zapewnia makro __arraycount()
w <sys/cdefs.h>
, co jest niebezpieczne, ponieważ brakuje mu parę nawiasów, ale możemy dodać te nawiasach siebie, a więc nawet nie trzeba pisać podział w naszym nagłówku (dlaczego chcemy powielić kod, który już istnieje? ). To makro jest zdefiniowane w nagłówku systemowym, więc jeśli go użyjemy, będziemy zmuszeni użyć powyższych makr.
#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))
Niektóre systemy zapewniają nitems()
w <sys/param.h>
zamian, a niektóre zapewniają oba. Powinieneś sprawdzić swój system i użyć tego, który posiadasz, a być może użyć niektórych warunków wstępnych procesora dla zapewnienia przenośności i obsługi obu.
Niestety ({})
rozszerzenia gcc nie można używać w zakresie plików. Aby móc korzystać z makra w zakresie plików, należy zapewnić statyczne potwierdzenie sizeof(struct {})
. Następnie pomnóż go, 0
aby nie wpłynąć na wynik. Rzutowanie na (int)
może być przydatne do symulacji funkcji, która zwraca (int)0
(w tym przypadku nie jest to konieczne, ale można ją wykorzystać do innych celów).
#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)
), które nie są pokazane: w innym miejscu ARRAY_BYTES(arr)
.
sizeof
, ale zamiast tego używać tych konstrukcji; jeśli masz ochotę pisać te konstrukcje za każdym razem, prawdopodobnie popełnisz błąd (bardzo częsty, jeśli kopiujesz wklej, a także bardzo powszechny, jeśli piszesz je za każdym razem, ponieważ mają wiele nawiasów) ...
sizeof
jest wyraźnie niebezpieczny (przyczyny są w odpowiedzi), i nie używa makr, ale korzysta z konstrukcji, które podałem za każdym razem, jest jeszcze bardziej niebezpieczny, więc jedyną drogą, aby przejść to makra.
„wprowadziłeś subtelny sposób strzelania sobie w stopę”
C Tablice „rodzime” nie przechowują swoich rozmiarów. Dlatego zaleca się zapisanie długości tablicy w osobnej zmiennej / const i przekazywanie jej za każdym razem, gdy przekazujesz tablicę, to znaczy:
#define MY_ARRAY_LENGTH 15
int myArray[MY_ARRAY_LENGTH];
NALEŻY zawsze unikać rodzimych tablic (chyba że nie możesz, w takim przypadku uważaj na swoją stopę). Jeśli piszesz w C ++, użyj kontenera „wektorowego” STL . „W porównaniu z tablicami zapewniają prawie taką samą wydajność” i są znacznie bardziej przydatne!
// 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
deklaracji.
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
Jeśli naprawdę chcesz to zrobić, aby ominąć tablicę, sugeruję zaimplementowanie struktury do przechowywania wskaźnika do typu, którego chcesz tablicę, oraz liczby całkowitej reprezentującej rozmiar tablicy. Następnie możesz przekazać to swoim funkcjom. Po prostu przypisz wartość zmiennej tablicowej (wskaźnik do pierwszego elementu) do tego wskaźnika. Następnie możesz przejść Array.arr[i]
do i-tego elementu i użyć, Array.size
aby uzyskać liczbę elementów w tablicy.
Podałem ci trochę kodu. Nie jest to bardzo przydatne, ale można go rozszerzyć o więcej funkcji. Szczerze mówiąc, jeśli tego właśnie chcesz, powinieneś przestać używać C i używać innego języka z wbudowanymi funkcjami.
/* 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);
nie obsługuje przepełnienia.
Najlepszym sposobem jest zapisanie tych informacji, na przykład w strukturze:
typedef struct {
int *array;
int elements;
} list_s;
Wdrażaj wszystkie niezbędne funkcje, takie jak tworzenie, niszczenie, sprawdzanie równości i wszystko, czego potrzebujesz. Łatwiej jest przekazać jako parametr.
int elements
vs size_t elements
?
Funkcja sizeof
zwraca liczbę bajtów używanych przez tablicę w pamięci. Jeśli chcesz obliczyć liczbę elementów w tablicy, powinieneś podzielić tę liczbę ze sizeof
zmiennym typem tablicy. Powiedzmy, że int array[10];
jeśli liczba całkowita typu zmiennego w twoim komputerze to 32 bity (lub 4 bajty), aby uzyskać rozmiar tablicy, wykonaj następujące czynności:
int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);
Możesz użyć &
operatora. Oto kod źródłowy:
#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;
};
Oto przykładowy wynik
1549216672
1549216712
---- diff----
4
The size of array a is 10
ptrdiff_t
. sizeof()
powoduje w size_t
. C nie określa, która jest szersza lub wyższa / ta sama ranga. Zatem rodzaj ilorazu ((char *)(&a+1)-(char *)a)/(sizeof(a[0]))
nie jest na pewno, size_t
a zatem drukowanie za pomocą z
może prowadzić do UB. Wystarczy samo użycie printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);
.
(char *)(&a+1)-(char *)a
nie jest stałą i może być obliczany w czasie wykonywania, nawet przy stałej wielkości a[10]
. sizeof(a)/sizeof(a[0])
w tym przypadku jest wykonywana stale w czasie kompilacji.
Bardziej eleganckim rozwiązaniem będzie
size_t size = sizeof(a) / sizeof(*a);
Oprócz udzielonych już odpowiedzi chcę wskazać szczególny przypadek zastosowania
sizeof(a) / sizeof (a[0])
Jeśli a
jest albo tablicą char
, unsigned char
albo signed char
nie trzeba używać sizeof
dwa razy, ponieważ zawsze występuje sizeof
wyrażenie z jednym operandem tego typu 1
.
Cytat z C18,6.5.3.4 / 4:
„ Gdy
sizeof
zostanie zastosowany do operandu, który ma typchar
,unsigned char
lubsigned char
(lub jego kwalifikowaną wersję), wynikiem jest1
.”
Zatem sizeof(a) / sizeof (a[0])
byłby równoważny z NUMBER OF ARRAY ELEMENTS / 1
if, jeśli a
jest tablicą typu char
, unsigned char
lub signed char
. Podział na 1 jest zbędny.
W takim przypadku możesz po prostu skrócić i wykonać:
sizeof(a)
Na przykład:
char a[10];
size_t length = sizeof(a);
Jeśli potrzebujesz dowodu, oto link do GodBolt .
Niemniej jednak dział utrzymuje bezpieczeństwo, jeśli typ znacznie się zmieni (chociaż przypadki te są rzadkie).
Uwaga: To może dać ci niezdefiniowane zachowanie wskazane przez MM w komentarzu.
int a[10];
int size = (*(&a+1)-a) ;
Aby uzyskać więcej informacji, zobacz tutaj i również tutaj .
*
operator nie może być stosowany do wskaźnika past-the-end
*(&a+1) - a;
różni się od (&a)[1] - a;
powyższego, czy nie oba *(&a+1)
i (&a)[1]
liczone jako 1 po zakończeniu?
x[y]
jest zdefiniowane jako*(x + (y))