W C, są tablice wskaźniki lub używane jako wskaźniki?
Zrozumiałem, że tablice są po prostu stałymi wskaźnikami do sekwencji wartości, a kiedy deklarowałeś tablicę w C, deklarowałeś wskaźnik i przydzielałeś przestrzeń dla sekwencji, do której ona wskazuje.
Ale to mnie myli: następujący kod:
char y[20];
char *z = y;
printf("y size is %lu\n", sizeof(y));
printf("y is %p\n", y);
printf("z size is %lu\n", sizeof(z));
printf("z is %p\n", z);
Po skompilowaniu z Apple GCC daje następujący wynik:
y size is 20
y is 0x7fff5fbff930
z size is 8
z is 0x7fff5fbff930
(moja maszyna ma 64 bity, wskaźniki mają długość 8 bajtów).
Jeśli' y ' jest stałym wskaźnikiem, dlaczego ma rozmiar 20, tak jak sekwencja wartości, na które wskazuje? Czy nazwa zmiennej " y " jest zastępowana przez adres pamięci podczas kompilacji, gdy jest odpowiednia? Czy tablice są więc jakimś cukrem składniowym w C, który po skompilowaniu jest tłumaczony na rzeczy pointer?
6 answers
Oto dokładny język ze standardu C ( n1256):
6.3.2.1 Lvalues, arrays, and function designators
...
3 z wyjątkiem sytuacji, gdy jest operandem operatorasizeof
lub operatora jednoargumentowego&
, lub jest ciągiem literalnym używanym do inicjalizacji tablicy, wyrażenie, które ma typ "array of type" jest konwertowane do wyrażenia z typem " pointer to type", które wskazuje na początkowy element obiektu array i nie jest lvalue. Jeśli obiekt array posiada klasę register storage, zachowanie jest niezdefiniowane.
Ważną rzeczą do zapamiętania jest to, że istnieje różnica między obiektem (w języku C, co oznacza coś, co zajmuje pamięć) a wyrażeniem używanym do odniesienia się do tego obiektu.
Gdy zadeklarujesz tablicę taką jak
int a[10];
Obiekt wyznaczony przez wyrażenie a
jest tablicą (tzn. sąsiadującym blokiem pamięci jest wystarczająco duży, aby pomieścić 10 int
wartości), a typ wyrażenia a to " 10-elementowa tablica int
" lub int [10]
. Jeśli wyrażenie a
pojawia się w kontekście innym niż operand operatorów sizeof
lub &
, wtedy jego typ jest domyślnie konwertowany na int *
, a jego wartością jest adres pierwszego elementu.
W przypadku operatora sizeof
, jeśli operand jest wyrażeniem typu T [N]
, to wynikiem jest liczba bajtów w obiekt array, a nie wskaźnik do tego obiektu: N * sizeof T
.
W przypadku operatora &
wartością jest adres tablicy, który jest taki sam jak adres pierwszego elementu tablicy, ale typ wyrażenia jest inny: biorąc pod uwagę deklarację T a[N];
, typ wyrażenia &a
to T (*)[N]
, lub wskaźnik do tablicy N-elementowej T. wartość jest taka sama jak a
lub &a[0]
(adres tablicy jest taki sam jak adres pierwszy element w tablicy), ale różnica typów ma znaczenie. Na przykład, biorąc pod uwagę kod
int a[10];
int *p = a;
int (*ap)[10] = &a;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
p++;
ap++;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
Zobaczysz wynik w kolejności
p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80
IOW, advancing p
dodaje sizeof int
(4) do pierwotnej wartości, podczas gdy advancing ap
dodaje 10 * sizeof int
(40).
Bardziej standardowy język:
6.5.2.1 array subscripting
ograniczenia
1 jedno z wyrażeń ma typ " wskaźnik do obiektu typ ", inne wyrażenie ma typ integer, a wynik ma typ " type ".
semantyka
2 wyrażenie postfix, po którym następuje wyrażenie w nawiasach kwadratowych[]
jest podpisanym oznaczeniem elementu obiektu array. Definicja operatora indeksu dolnego[]
jest taka, żeE1[E2]
jest identyczny z(*((E1)+(E2)))
. Ze względu na reguły konwersji, które mają zastosowanie do operatora binarnego+
, jeśliE1
jest obiektem tablicy (równoważnie wskaźnikiem do element początkowy obiektu tablicy) iE2
jest liczbą całkowitą,E1[E2]
wyznaczaE2
- ten elementE1
(licząc od zera).
Tak więc, kiedy indeksujesz wyrażenie tablicy, to pod maską dzieje się to, że przesunięcie od adresu pierwszego elementu tablicy jest obliczane, a wynik jest dereferowany. Wyrażenie
a[i] = 10;
Jest równoważne
*((a)+(i)) = 10;
Co jest równoważne
*((i)+(a)) = 10;
Co jest równoważne do
i[a] = 10;
Tak, zapisywanie tablicy w C jest przemienne; na miłość boską, nigdy nie rób tego w kodzie produkcyjnym.
Ponieważ array subscripting jest zdefiniowany w kategoriach operacji wskaźnika, można zastosować operator indeksu do wyrażeń typu wskaźnika oraz typu tablicy:
int *p = malloc(sizeof *p * 10);
int i;
for (i = 0; i < 10; i++)
p[i] = some_initial_value();
Oto przydatna tabela, aby zapamiętać niektóre z tych pojęć:]}
Declaration: T a[N]; Expression Type Converts to Value ---------- ---- ------------ ----- a T [N] T * Address of the first element in a; identical to writing &a[0] &a T (*)[N] Address of the array; value is the same as above, but the type is different sizeof a size_t Number of bytes contained in the array object (N * sizeof T) *a T Value at a[0] a[i] T Value at a[i] &a[i] T * Address of a[i] Declaration: T a[N][M]; Expression Type Converts to Value ---------- ---- ------------ ----- a T [N][M] T (*)[M] Address of the first subarray (&a[0]) &a T (*)[N][M] Address of the array (same value as above, but different type) sizeof a size_t Number of bytes contained in the array object (N * M * sizeof T) *a T [M] T * Value of a[0], which is the address of the first element of the first subarray (same as &a[0][0]) a[i] T [M] T * Value of a[i], which is the address of the first element of the i'th subarray &a[i] T (*)[M] Address of the i-th subarray; same value as above, but different type sizeof a[i] size_t Number of bytes contained in the i'th subarray object (M * sizeof T) *a[i] T Value of the first element of the i'th subarray (a[i][0]) a[i][j] T Value at a[i][j] &a[i][j] T * Address of a[i][j] Declaration: T a[N][M][O]; Expression Type Converts to ---------- ---- ----------- a T [N][M][O] T (*)[M][O] &a T (*)[N][M][O] *a T [M][O] T (*)[O] a[i] T [M][O] T (*)[O] &a[i] T (*)[M][O] *a[i] T [O] T * a[i][j] T [O] T * &a[i][j] T (*)[O] *a[i][j] T a[i][j][k] T
Stąd wzór dla tablic wyższych wymiarów powinien być jasny.
Tak więc w podsumowaniu: tablice nie są wskaźnikami. W większości kontekstów wyrażenia array są konwertowane na typy wskaźników.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-05 19:36:14
Tablice nie są wskaźnikami, chociaż w większości wyrażeń nazwa tablicy jest obliczana jako wskaźnik do pierwszego elementu tablicy. Jest więc bardzo, bardzo łatwo używać nazwy tablicy jako wskaźnika. Często zobaczysz termin 'decay' używany do opisania tego, jak w "tablica rozpadła się do wskaźnika".
Jedynym wyjątkiem jest operand do operatora sizeof
, Gdzie wynikiem jest rozmiar tablicy (w bajtach, a nie elementach).
Kilka dodatkowych kwestii związanych z tym:
An parametr array do funkcji jest fikcją-kompilator przekazuje zwykły wskaźnik (Nie dotyczy to parametrów reference-to-array w C++), więc nie możesz określić rzeczywistego rozmiaru tablicy przekazywanej do funkcji - musisz przekazać tę informację w inny sposób (być może używając jawnego dodatkowego parametru lub używając elementu sentinel-podobnie jak ciągi C)
Również, powszechnym idiomem, aby uzyskać liczbę elementów w tablicy, jest użycie makra takiego jak:
#define ARRAY_SIZE(arr) ((sizeof(arr))/sizeof(arr[0]))
To ma problem z zaakceptowaniem nazwy tablicy, gdzie będzie działać, lub wskaźnika, gdzie da nonsensowny wynik bez ostrzeżenia ze strony kompilatora. Istnieją bezpieczniejsze wersje makra (szczególnie dla C++), które generują ostrzeżenie lub błąd, gdy są używane ze wskaźnikiem zamiast tablicy. Zobacz następujące pozycje:
Uwaga: C99 VLAs (tablice o zmiennej długości) mogą nie spełniać wszystkich tych reguł (w szczególności mogą być przekazywane jako parametry o rozmiarze tablicy znanym przez wywołaną funkcję). Mam małe doświadczenie z Vlasem i z tego co wiem, nie są one powszechnie używane. Chciałbym jednak zaznaczyć, że powyższa dyskusja może mieć inne zastosowanie do VLAs.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:09:24
sizeof
jest obliczana w czasie kompilacji, a kompilator wie, czy operand jest tablicą czy wskaźnikiem. Dla tablic podaje liczbę bajtów zajętych przez tablicę. Twoja tablica jest char[]
(i sizeof(char)
jest 1), więc sizeof
daje Ci liczbę elementów. Aby uzyskać liczbę elementów w przypadku ogólnym, wspólnym idiomem jest (tutaj dla int
):
int y[20];
printf("number of elements in y is %lu\n", sizeof(y) / sizeof(int));
Dla wskaźników sizeof
podaje liczbę bajtów zajętych przez typ wskaźnika raw.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-05 17:43:48
Oprócz tego, co powiedzieli inni, może ten artykuł pomoże: http://en.wikipedia.org/wiki/C_%28programming_language%29#Array-pointer_interchangeability
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-05 17:31:36
W
char hello[] = "hello there"
int i;
I
char* hello = "hello there";
int i;
W pierwszym przypadku (wyrównanie dysków) 12 bajtów zostanie zapisanych dla hello z przydzieloną przestrzenią inicjalizowaną na hello there, podczas gdy w drugim hello there jest zapisywane gdzie indziej (ewentualnie statyczna przestrzeń) i hello
jest inicjalizowana tak, aby wskazywała na podany łańcuch.
hello[2]
podobnie jak *(hello + 2)
zwróci "e" w obu przypadkach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-05 17:44:20
Jeśli' y ' jest stałym wskaźnikiem, dlaczego ma rozmiar 20, tak jak sekwencja wartości, na którą wskazuje?
Ponieważ z
jest adresem zmiennej i zawsze zwróci 8 dla twojej maszyny. Aby uzyskać zawartość zmiennej, musisz użyć wskaźnika dereference ( & ).
EDIT: dobre rozróżnienie między tymi dwoma: http://www.cs.cf.ac.uk/Dave/C/node10.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-05 17:40:28