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]
a[5] == 5[a]
? 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).
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.
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, przedunsigned
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 .
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)
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ę.
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
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.
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:)
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].
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&);
};
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ć zi[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';
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.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.
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)
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)
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
.
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*.
a[5] == *(a +5) == *(5 + a) == 5[a]
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