Dlaczego w przypadku tablic a [5] = = 5[a]?

Jak wskazuje Joel w Stack Overflow podcast #34 , w C język programowania (aka: K & R), jest wzmianka o tej właściwości tablic w C: a[5] == 5[a]

Joel mówi, że to przez arytmetykę wskaźnika, ale nadal nie rozumiem. dlaczego a[5] == 5[a]?
Author: Lundin, 2008-12-19

17 answers

Norma C definiuje operator [] w następujący sposób:

a[b] == *(a + b)

Dlatego a[5] oceni do:

*(a + 5)

I 5[a] ocenią do:

*(5 + a)

a jest wskaźnikiem do pierwszego elementu tablicy. a[5] jest wartością, która wynosi 5 elementów dalej od a, która jest taka sama jak *(a + 5), a z matematyki szkoły podstawowej wiemy, że są one równe (dodawanie jest przemienne).

 1723
Author: Mehrdad Afshari,
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-11-30 10:47:52

Ponieważ dostęp do tablicy jest zdefiniowany w kategoriach wskaźników. a[i] jest zdefiniowane jako *(a + i), co jest przemienne.

 273
Author: David Thornley,
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-05-13 13:47:10

Myślę, że coś jest pominięte przez inne odpowiedzi.

Tak, p[i] jest z definicji równoważne *(p+i), Które (ponieważ dodawanie jest przemienne) jest równoważne *(i+p), które (ponownie, zgodnie z definicją operatora []) jest równoważne i[p].

(A w array[i] nazwa tablicy jest domyślnie przekonwertowana na wskaźnik do pierwszego elementu tablicy.)

Ale komutatywność dodawania nie jest w tym przypadku aż tak oczywista.

Gdy oba operandy są tego samego typu, a nawet różnych typów liczbowych, które są promowane do wspólnego typu, komutatywność ma sens: x + y == y + x.

Ale w tym przypadku mówimy konkretnie o arytmetyce wskaźników, gdzie jeden operand jest wskaźnikiem, a drugi liczbą całkowitą. (Integer + integer to inna operacja, a pointer + pointer to nonsens.)

Opis standardu C operatora+ (N1570 6.5.6) mówi:

Do dodawania, albo oba operandy mają typ arytmetyczny lub jeden operand jest wskaźnikiem do kompletnego typu obiektu, a drugi ma typ integer.

Równie łatwo można było powiedzieć:

Dla dodawania oba operandy mają typ arytmetyczny, albo lewy operand jest wskaźnikiem do kompletnego typu obiektu i prawym operandem ma typ integer.

W takim przypadku zarówno i + p jak i i[p] byłyby nielegalne.

W języku C++ tak naprawdę mamy dwa zestawy przeciążonych operatorów +, które można luźno opisać jako:

pointer operator+(pointer p, integer i);

I

pointer operator+(integer i, pointer p);

Z czego tylko pierwszy jest naprawdę potrzebny.

Więc dlaczego tak jest?

C++ odziedziczył tę definicję od C, który otrzymał ją od B (komutatywność indeksowania tablicy jest wyraźnie wymieniona w 1972 odniesienie użytkowników do B ), który otrzymał ją od BCPL (podręcznik z 1967 roku), który może cóż, dostali go z jeszcze wcześniejszych języków (CPL? Algol?).

Więc idea, że indeksowanie tablic jest zdefiniowane jako dodawanie, i że dodawanie, nawet wskaźnika i liczby całkowitej, jest przemienne, sięga wielu dekad wstecz, do języków przodków C.

Języki te były znacznie słabiej pisane niż współczesne C. W szczególności często ignorowano rozróżnienie między wskaźnikami i liczbami całkowitymi. (Wczesni Programiści C czasami używali wskaźników jako niepodpisanych liczb całkowitych, przed unsigned słowo kluczowe zostało dodane do języka.) Więc pomysł, aby dodawanie nie było przemienne, ponieważ operandy są różnych typów, prawdopodobnie nie przyszło by na myśl projektantom tych języków. Jeśli użytkownik chciał dodać dwie "rzeczy", czy te" rzeczy " są liczbami całkowitymi, wskaźnikami lub czymś innym, to nie od języka zależało, aby temu zapobiec.

I z biegiem lat, każda zmiana tej reguły byłaby złamaniem istniejącego kodu (choć Standard ANSI C z 1989 roku mógł być dobrym okazja).

Zmiana C i / lub c++ w taki sposób, aby wymagała umieszczenia wskaźnika po lewej stronie i liczby całkowitej po prawej stronie, może spowodować uszkodzenie istniejącego kodu, ale nie spowoduje utraty rzeczywistej siły ekspresji.

Więc teraz mamy arr[3] i 3[arr] znaczenie dokładnie to samo, choć ta ostatnia forma nigdy nie powinna pojawić się poza IOCCC .

 190
Author: Keith Thompson,
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-01-28 20:48:59

I oczywiście

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

GĹ 'Ăłwnym powodem tego byĹ' o to, Ĺźe w latach 70-tych, kiedy projektowano C, komputery nie miaĹ 'y zbyt wiele pamiÄ ™ ci (64KB to duĹźo), wiÄ ™ c kompilator C nie sprawdzaĺ ' zbyt wiele skĹ ' adni. Stąd "X[Y] " zostało raczej ślepo przetłumaczone na "*(X+Y)"

Wyjaśnia to również składnię" += "i" ++". Wszystko w formie "A = B + C " miało taką samą skompilowaną formę. Ale jeśli B był tym samym obiektem co a, wtedy dostępna była optymalizacja poziomu złożenia. Ale kompilator nie był wystarczająco jasny, aby go rozpoznać, więc programista musiał (A += C). Podobnie, Jeśli C było 1, dostępna była inna optymalizacja poziomu montażu i programista musiał to wyraźnie wyrazić, ponieważ kompilator go nie rozpoznał. (Ostatnio Kompilatory tak robią, więc te składnie są w dzisiejszych czasach w dużej mierze niepotrzebne)

 185
Author: James Curran,
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-07-28 17:51:58

Jedna rzecz, o której nikt nie wspomniał o problemie Dinah z sizeof:

Możesz dodać tylko liczbę całkowitą do wskaźnika, nie możesz dodać dwóch wskaźników razem. W ten sposób podczas dodawania wskaźnika do liczby całkowitej lub liczby całkowitej do wskaźnika kompilator zawsze wie, który bit ma rozmiar, który należy wziąć pod uwagę.

 51
Author: user30364,
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-12-18 08:01:42

Aby odpowiedzieć na pytanie dosłownie. Nie zawsze jest prawdą, że x == x

double zero = 0.0;
double a[] = { 0,0,0,0,0, zero/zero}; // NaN
cout << (a[5] == 5[a] ? "true" : "false") << endl;

Druki

false
 47
Author: Peter Lawrey,
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-08-11 13:50:22

Ładne pytanie/odpowiedzi.

Chcę tylko zaznaczyć, że wskaźniki i tablice C nie są tym samym, chociaż w tym przypadku różnica nie jest istotna.

Rozważmy następujące deklaracje:

int a[10];
int* p = a;

In A.out, symbol a znajduje się pod adresem, który jest początkiem tablicy, a symbol P znajduje się pod adresem, w którym przechowywany jest wskaźnik, a wartość wskaźnika w tym miejscu pamięci jest początkiem tablicy.

 23
Author: PolyThinker,
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
2008-12-20 08:16:20

Po prostu dowiedziałem się, że ta brzydka składnia może być "przydatna", a przynajmniej bardzo zabawna do zabawy, gdy chcesz poradzić sobie z tablicą indeksów, które odnoszą się do pozycji w tej samej tablicy. Może zastąpić zagnieżdżone nawiasy kwadratowe i uczynić kod bardziej czytelnym !

int a[] = { 2 , 3 , 3 , 2 , 4 };
int s = sizeof a / sizeof *a;  //  s == 5

for(int i = 0 ; i < s ; ++i) {  

           cout << a[a[a[i]]] << endl;
           // ... is equivalent to ... 
           cout << i[a][a][a] << endl;  // but I prefer this one, it's easier to increase the level of indirection (without loop)

}

Oczywiście, jestem pewien, że w prawdziwym kodzie nie ma zastosowania, ale i tak uważam, że jest to interesujące:)

 21
Author: Frédéric Terrazzoni,
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
2012-06-10 19:50:54

Dla wskaźników w C mamy

a[5] == *(a + 5)

A także

5[a] == *(5 + a)

Stąd prawdą jest, że a[5] == 5[a].

 17
Author: user1055604,
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
2012-03-23 17:36:57

Nie odpowiedź, ale coś do przemyślenia. Jeśli klasa ma przeciążony operator indeksu / indeksu, wyrażenie 0[x] nie będzie działać:

class Sub
{
public:
    int operator [](size_t nIndex)
    {
        return 0;
    }   
};

int main()
{
    Sub s;
    s[0];
    0[s]; // ERROR 
}

Ponieważ nie mamy dostępu doint klasy, nie można tego zrobić:

class int
{
   int operator[](const Sub&);
};
 14
Author: Ajay,
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-12-09 18:29:15

Ma bardzo dobre wyjaśnienie w samouczku na wskaźnikach i tablicach w C przez Teda Jensena.

Ted Jensen wyjaśnił to jako:

W rzeczywistości jest to prawda, tzn. gdziekolwiek się pisze {[3] } może być zastąpiony *(a + i) bez żadnych problemów. W rzeczywistości kompilator utworzy ten sam kod w obu przypadkach. Widzimy więc, że wskaźnik arytmetyka jest tym samym co indeksowanie tablicy. Albo składnia wytwarza ten sam wynik.

To nie jest powiedzenie że wskaźniki i tablice są tym samym, nie są. Mówimy to tylko po to, by zidentyfikować danego elementu tablicy mamy do wyboru dwie składnie, jedną za pomocą indeksowania tablic, a drugi za pomocą arytmetyki wskaźników, które uzyskaj identyczne wyniki.

Teraz, patrząc na to ostatnie ekspresja, część tego.. (a + i), jest prostym dodatkiem używającym + operator i reguły C stwierdzają, że takie wyrażenie jest / align = "left" / To znaczy (a + i) jest identyczne z (i + a). Tak więc my może napisz *(i + a) tak łatwo jak *(a + i). Ale *(i + a) mogło pochodzić z i[a] ! Z tego wszystkiego pochodzi ciekawy prawda, że jeśli:

char a[20];

Pisanie

a[3] = 'x';

To to samo co pisanie

3[a] = 'x';
 9
Author: A.s. Bhullar,
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-01-13 06:00:35

Wiem, że odpowiedź na to pytanie została udzielona, ale nie mogłem oprzeć się temu wyjaśnieniu.

Pamiętam zasady projektowania kompilatorów, Załóżmy, że a jest tablicą int, A rozmiar int wynosi 2 bajty, & Adres bazowy dla a to 1000.

Jak będzie działać a[5]- >

Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

Więc,

Podobnie, gdy kod c jest podzielony na 3-adresowy kod, 5[a] stanie się- >

Base Address of your Array a + (size of(data type for array a)*5)
i.e. 1000 + (2*5) = 1010 

Więc w zasadzie oba stwierdzenia wskazują na to samo miejsce w pamięć i stąd a[5] = 5[a].

To wyjaśnienie jest również powodem, dla którego ujemne indeksy w tablicach działają w C.

Tzn. jeśli uzyskam dostęp a[-5] to da mi

Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990
Zwróci mi obiekt w miejscu 990.
 6
Author: Ajinkya Patil,
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-11-29 01:18:48

W tablicach C, arr[3] i 3[arr] są takie same, a ich równoważne oznaczenia wskaźników to *(arr + 3) do *(3 + arr). Ale przeciwnie [arr]3 lub [3]arr nie jest poprawne i spowoduje błąd składni, ponieważ (arr + 3)* i (3 + arr)* nie są poprawnymi wyrażeniami. Powodem jest to, że operator dereferencji powinien być umieszczony przed adresem otrzymanym przez wyrażenie, a nie po adresie.

 4
Author: Krishan,
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-12-17 11:22:08

W kompilatorze c

a[i]
i[a]
*(a+i)

To różne sposoby odwoływania się do elementu w tablicy ! (WCALE NIE DZIWNE)

 4
Author: AVIK DUTTA,
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-10-29 09:14:54

W C

 int a[]={10,20,30,40,50};
 int *p=a;
 printf("%d\n",*p++);//output will be 10
 printf("%d\n",*a++);//will give an error

Wskaźnik jest "zmienną"

Nazwa tablicy jest "mnemonicznym" lub "synonimem"

p++; is valid but a++ is invalid

a[2] jest równa 2 [a], ponieważ wewnętrzna operacja na obu z nich to

"arytmetyka wskaźnika" obliczana wewnętrznie jako

*(a+3) equals *(3+a)

 0
Author: Jayghosh Wankar,
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-01-07 09:49:53

Cóż, jest to funkcja, która jest możliwa tylko dzięki obsłudze języka.

Kompilator interpretuje a[i] jako *(a+i), A wyrażenie 5[a] ocenia na *(5+a). Ponieważ dodawanie jest przemienne, okazuje się, że oba są równe. Stąd wyrażenie jest ewaluowane do true.

 0
Author: Harsha JK,
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 18:42:27

Typy wskaźników

1) Wskaźnik do danych

int *ptr;

2) Wskaźnik const do danych

int const *ptr;

3) wskaźnik const do danych const

int const *const ptr;

I tablice są typu (2) z naszej listy
Kiedy zdefiniujesz tablicę w czasie, jeden adres jest inicjalizowany w tym wskaźniku
Ponieważ wiemy, że nie możemy zmienić lub zmodyfikować wartości const w naszym programie, ponieważ wyrzuca on błąd w czasie kompilacji

Główna różnica I znaleziony jest...

Możemy ponownie zainicjować wskaźnik przez adres, ale nie ten sam przypadek z tablicą.

======
wracając do pytania...
a [5] to nic innego jak *(a + 5)
możesz łatwo zrozumieć przez
a-zawierający adres (ludzie nazywają go adresem bazowym), podobnie jak typ (2) wskaźnika na naszej liście
[]- operator ten można zastąpić wskaźnikiem*.

W końcu...
a[5] == *(a +5) == *(5 + a) == 5[a] 
 -2
Author: Jeet Parikh,
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-07-13 07:34:11