Czy nazwa tablicy jest wskaźnikiem?

Czy nazwa tablicy jest wskaźnikiem w C? Jeśli nie, Jaka jest różnica między nazwą tablicy a zmienną wskaźnika?

Author: Lundin, 2009-10-29

9 answers

Tablica jest tablicą, a wskaźnik jest wskaźnikiem, ale w większości przypadków nazwy tablic są konwertowane na wskaźniki. Często używane jest określenie, że rozpadają się na wskaźniki.

Oto tablica:

int a[7];

A zawiera przestrzeń dla siedmiu liczb całkowitych i możesz umieścić wartość w jednej z nich za pomocą przypisania, tak:

a[3] = 9;

Oto wskaźnik:

int *p;

P nie zawiera spacji dla liczb całkowitych, ale może wskazywać na spację dla liczba całkowita. Możemy, na przykład, ustawić go tak, aby wskazywał na jedno z miejsc w tablicy a , takie jak pierwsze:

p = &a[0];

Co może być mylące jest to, że możesz również napisać to:

p = a;

To nie kopiuje zawartość tablicy a {[16] } do wskaźnika p (cokolwiek by to oznaczało). Zamiast tego Nazwa tablicy a jest konwertowana na wskaźnik do pierwszego elementu. Więc to zadanie robi to samo, co poprzednie.

Teraz ty może używać p w sposób podobny do tablicy:

p[3] = 17;

Powodem tego działania jest to, że operator dereferencji tablicy w C, " []", jest zdefiniowany w kategoriach wskaźników. x[y] oznacza: zacznij od wskaźnika x , krok y elementy do przodu po tym, co wskazuje wskaźnik, a następnie weź to, co tam jest. Używając składni arytmetycznej wskaźnika, x [y] można również zapisać jako *(x+y).

Aby to działało z normalną tablicą, taką jak nasza a , Nazwa a w a [3] musi być najpierw przekonwertowana na wskaźnik (do pierwszego elementu w a). Następnie krok 3 elementy do przodu, i wziąć to, co tam jest. Innymi słowy: weź element na pozycji 3 w tablicy. (Który jest czwartym elementem tablicy, ponieważ pierwszy ma numer 0.)

Podsumowując, nazwy tablic w programie C są (w większości przypadków) konwertowane na wskaźniki. Wyjątek stanowi użycie operatora sizeof na tablica. Jeśli a zostanie przekonwertowany na wskaźnik w tym kontekście, sizeof (a) da rozmiar wskaźnika, a nie rzeczywistej tablicy, co byłoby raczej bezużyteczne, więc w tym przypadku a oznacza samą tablicę.

 210
Author: Thomas Padron-McCarthy,
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
2016-10-12 05:58:45

Gdy tablica jest używana jako wartość, jej nazwa reprezentuje adres pierwszego elementu.
Gdy tablica nie jest używana jako wartość, jej nazwa przedstawia całą tablicę.

int arr[7];

/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */

/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */
 29
Author: pmg,
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
2013-06-04 20:24:39

Jeżeli wyrażenie typu array (takie jak nazwa tablicy) pojawia się w większym wyrażeniu i nie jest operandem operatorów & lub sizeof, wtedy typ wyrażenia array jest konwertowany z "N-elementowej tablicy T" na "wskaźnik na T", a wartością wyrażenia jest adres pierwszego elementu w tablicy.

W skrócie, nazwa tablicy nie jest wskaźnikiem, ale w większości kontekstów jest traktowana tak, jakby była wskaźnikiem.

Edit

Odpowiedź na pytanie w komentarzu:

Jeśli używam sizeof, czy zliczam rozmiar tylko elementów tablicy? Wtedy tablica "head" zajmuje również miejsce z informacją o długości i wskaźniku(a to oznacza, że zajmuje więcej miejsca niż normalny wskaźnik)?

Kiedy tworzysz tablicę, jedyną przydzieloną przestrzenią jest przestrzeń dla samych elementów. oddzielny wskaźnik lub dowolne metadane. Given

char a[10];

To co masz w pamięci to

   +---+
a: |   | a[0]
   +---+ 
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...
   +---+
   |   | a[9]
   +---+

Wyrażenie a odnosi się do całej tablicy, ale nie ma obiektu a oddzielić od samych elementów tablicy. Tak więc sizeof a daje rozmiar (w bajtach)całej tablicy. Wyrażenie &a daje adres tablicy , który jest taki sam jak adres pierwszego elementu. Różnica pomiędzy &a i &a[0] jest rodzajem wynik1 - int (*)[10] w pierwszym przypadku i int * w drugim.

Gdzie robi się dziwnie jest wtedy, gdy chcesz uzyskać dostęp do poszczególnych elementów-wyrażenie a[i] jest zdefiniowane jako wynik *(a + i) - biorąc pod uwagę wartość adresu a, przesuń i elementy ( Nie bajty) z tego adresu i usuń wynik.

Problem polega na tym, że a nie jest wskaźnikiem ani adresem - jest to cały obiekt array. Tak więc reguła w C, że gdy kompilator widzi wyrażenie typu array (np. a, które ma typ char [10]) i to wyrażenie nie jest operandem operatorów sizeof lub unary &, Typ tego wyrażenia jest konwertowany ("rozpada się") na typ wskaźnika (char *), a wartość wyrażenia jest adresem pierwszego elementu tablicy. Zatem wyrażenie a ma ten sam typ i wartość co wyrażenie &a[0] (i przez rozszerzenie, wyrażenie *a ma ten sam typ i wartość co wyrażenie a[0]).

C pochodzi z wcześniejszego języka zwanego B, A W B a był oddzielnym obiektem wskaźnika od elementów tablicy a[0], a[1], itd. Ritchie chciał zachować semantykę tablicy B, ale nie chciał mieszać z przechowywaniem oddzielnego obiektu wskaźnika. Więc się go pozbył. Zamiast tego kompilator przekonwertuje wyrażenia tablicowe na wyrażenia wskaźnikowe podczas tłumaczenia w razie potrzeby.

Pamiętaj, że powiedziałem, że tablice nie przechowują żadnych metadane dotyczące ich wielkości. Gdy tylko wyrażenie tablicy "rozpada się" na wskaźnik, wszystko, co masz, to wskaźnik do pojedynczego elementu. Element ten może być pierwszym z sekwencji elementów lub może być pojedynczym obiektem. Nie ma sposobu, aby wiedzieć na podstawie samego wskaźnika.

Kiedy przekazujesz wyrażenie tablicy do funkcji, wszystko, co otrzymuje funkcja, jest wskaźnikiem do pierwszego elementu - nie ma pojęcia, jak duża jest tablica (dlatego funkcja gets była takim zagrożeniem i była ostatecznie usunięty z biblioteki). Aby funkcja wiedziała, ile elementów ma tablica, musisz użyć wartości sentinel (takiej jak terminator 0 w łańcuchach C) lub musisz przekazać liczbę elementów jako oddzielny parametr.


  1. które * może * wpływać na interpretację wartości adresu-zależy od maszyny.

 10
Author: John Bode,
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-12-08 01:08:17

Tablica zadeklarowana w ten sposób

int a[10];

Przydziela pamięć na 10 ints. nie możesz modyfikować a, ale możesz arytmetykę wskaźnika za pomocą a.

Taki wskaźnik przydziela pamięć tylko dla wskaźnika p:

int *p;

Nie przydziela żadnych ints. możesz go zmodyfikować:

p = a;

I użyj array subscripts, jak możesz za pomocą:

p[2] = 5;
a[2] = 5;    // same
*(p+2) = 5;  // same effect
*(a+2) = 5;  // same effect
 5
Author: Grumdrig,
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-06-03 16:37:37

Nazwa tablicy sama w sobie daje lokalizację pamięci, więc można traktować ją jak wskaźnik:

int a[7];

a[0] = 1976;
a[1] = 1984;

printf("memory location of a: %p", a);

printf("value at memory location %p is %d", a, *a);

I inne fajne rzeczy, które możesz zrobić ze wskaźnikiem( np. dodając / odejmując offset), możesz również zrobić z tablicą:

printf("value at memory location %p is %d", a + 1, *(a + 1));

Pod względem językowym, jeśli C nie ujawnia tablicy jako tylko jakiegoś "wskaźnika" (pedantycznie jest to tylko lokalizacja pamięci. Nie może wskazywać na dowolne położenie w pamięci, ani być sterowane przez programistę). Zawsze musimy kodować to:

printf("value at memory location %p is %d", &a[1], a[1]);
 4
Author: Michael Buen,
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
2009-10-29 15:03:24

Myślę, że ten przykład rzuca trochę światła na ten temat:

#include <stdio.h>
int main()
{
        int a[3] = {9, 10, 11};
        int **b = &a;

        printf("a == &a: %d\n", a == b);
        return 0;
}

Kompiluje fine (z 2 ostrzeżeniami) w gcc 4.9.2 i wypisuje:

a == &a: 1

Oops: -)

Więc wniosek jest taki: nie, tablica nie jest wskaźnikiem, nie jest przechowywana w pamięci (nawet nie tylko do odczytu) jako wskaźnik, nawet jeśli wygląda na to, że jest, ponieważ można uzyskać jej adres za pomocą operatora&. Ale-UPS - ten operator nie działa : -)), tak czy siak, zostałeś ostrzeżony:

p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
  int **b = &a;
            ^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
  printf("a == &a: %d\n", a == b);

C++ odmawia wszelkich takich prób z błędami w czasie kompilacji.

Edit:

Oto co chciałem zademonstrować:

#include <stdio.h>
int main()
{
    int a[3] = {9, 10, 11};
    void *c = a;

    void *b = &a;
    void *d = &c;

    printf("a == &a: %d\n", a == b);
    printf("c == &c: %d\n", c == d);
    return 0;
}

Mimo że c i a "wskazują" na tę samą pamięć, można uzyskać adres wskaźnika c, ale nie można uzyskać adresu wskaźnika a.

 0
Author: Palo,
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
2018-04-02 21:07:50

Nazwa tablicy zachowuje się jak wskaźnik i wskazuje na pierwszy element tablicy. Przykład:

int a[]={1,2,3};
printf("%p\n",a);     //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0

Obie instrukcje print dają dokładnie to samo wyjście dla maszyny. W moim systemie dał:

0x7fff6fe40bc0
 -3
Author: Amitesh Ranjan,
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
2014-12-01 17:59:12

Tablica jest zbiorem elementów secuentialnych i przylegających do siebie w pamięci. W C Nazwa tablicy jest indeksem pierwszego elementu, a stosując przesunięcie można uzyskać dostęp do reszty elementów. "Indeks do pierwszego elementu" jest rzeczywiście wskaźnikiem kierunku pamięci.

Różnica ze zmiennymi wskaźnika polega na tym, że nie można zmienić lokalizacji, do której wskazuje nazwa tablicy, więc jest ona podobna do wskaźnika const (jest podobna, nie taka sama. Zobacz komentarz Marka). Ale także, że ty nie trzeba dereferować nazwy tablicy, aby uzyskać wartość, jeśli używasz aritmetic wskaźnika:

char array = "hello wordl";
char* ptr = array;

char c = array[2]; //array[2] holds the character 'l'
char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l'
Więc odpowiedź brzmi "tak".
 -4
Author: Ricardo Amores,
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-03-22 20:41:42

Nazwa tablicy jest adresem 1. elementu tablicy. Tak więc tak Nazwa tablicy jest wskaźnikiem const.

 -5
Author: SAQIB SOHAIL BHATTI,
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
2015-09-28 11:14:37