Що стосується масивів, чому так буває, що a [5] == 5 [a]?


1621

Як Джоел зазначає у підкасті № 34 Stack Overflow , у програмі мови програмування C (він же: K&R), про цю властивість масивів у C згадується:a[5] == 5[a]

Джоель каже, що це через арифметику вказівника, але я все ще не розумію. Чому такa[5] == 5[a] ?


48
чи щось на зразок [+] також працює як * (a ++) АБО * (++ a)?
Егон

45
@Egon: Це дуже креативно, але, на жаль, не так працюють компілятори. Компілятор інтерпретує a[1]як ряд лексем, а не рядків: * ({ціле розташування} a {оператор} + {ціле число} 1) те саме, що * ({integer} 1 {оператор} + {ціле розташування} a) але не є тим самим, як * ({integer location of} a {operator} + {operator} +)
Діна

11
Цікава зміна цього варіанту проілюстрована у доступі до Ілогічного масиву , де Ви маєте char bar[]; int foo[];та foo[i][bar]використовується як вираз.
Джонатан Леффлер

5
@EldritchConundrum, чому ви вважаєте, що "компілятор не може перевірити, чи ліва частина є вказівником"? Так, це може. Це правда, що a[b]= *(a + b)для будь-якого даного aі b, але саме вільний вибір дизайнерів мови +повинен бути визначений комутативним для всіх типів. Ніщо не може завадити їм заборонити i + p, дозволяючи p + i.
ач

13
@Andrey Один зазвичай розраховує +на комутативний характер, тому, можливо, справжньою проблемою є вибір того, щоб зробити покажчикові операції схожими на арифметичні, а не розробити окремий оператор зміщення.
Eldritch Conundrum

Відповіді:


1924

Стандарт C визначає []оператора наступним чином:

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

Тому a[5]оцінюємо:

*(a + 5)

і 5[a]оцінить:

*(5 + a)

aє вказівником на перший елемент масиву. a[5]це значення , яке це 5 елементів далі від a, який є таким же , як *(a + 5)і від початкової школи математики ми знаємо , ті , рівні (додавання коммутативное ).


325
Цікаво, чи він не схожий на * ((5 * sizeof (a)) + a). Хоча велике пояснення.
Джон Макінтайр

92
@Dinah: З точки зору компілятора C, ти маєш рацію. Не потрібний жодний розмір, і ті вирази, про які я згадував, - ТОЖЕ. Однак компілятор враховує розмірofof при створенні машинного коду. Якщо a - це int масив, a[5]компілюється на щось на зразок mov eax, [ebx+20]замість[ebx+5]
Mehrdad Afshari

12
@Dinah: A - це адреса, скажімо, 0x1230. Якщо a був у 32-бітному масиві int, то a [0] знаходиться у 0x1230, a [1] у 0x1234, a [2] у 0x1238 ... a [5] у x1244 тощо. Якщо ми просто додамо 5 до 0x1230, ми отримуємо 0x1235, що неправильно.
Джеймс Курран

36
@ sr105: Це особливий випадок для оператора +, де один з операндів - покажчик, а інший - ціле число. Стандарт говорить, що результат буде мати тип вказівника. Компілятор / повинен бути / досить розумним.
AIB

48
"З математики початкової школи ми знаємо, що вони рівні" - Я розумію, що ви спрощуєте, але я з тими, хто вважає, що це над спрощенням. Це не елементарно *(10 + (int *)13) != *((int *)10 + 13). Іншими словами, тут відбувається більше, ніж арифметика початкової школи. Комутативність критично покладається на компілятор, який визнає, який операнд є вказівником (і на який розмір об'єкта). По-іншому (1 apple + 2 oranges) = (2 oranges + 1 apple), але (1 apple + 2 oranges) != (1 orange + 2 apples).
LarsH

288

Оскільки доступ до масиву визначається за допомогою покажчиків. a[i]визначається як значення *(a + i), яке є комутативним.


42
Масиви не визначаються з точки зору покажчиків, але доступ до них є.
Гонки легкості по орбіті

5
Я б додав "так це дорівнює *(i + a), що можна записати як i[a]".
Джим Балтер

4
Я б запропонував вам включити цитату зі стандарту, яка полягає в наступному: 6.5.2.1: 2 Постфікс-вираз з наступним виразом у квадратних дужках [] - це підписане позначення елемента об’єкта масиву. Визначення оператора індексації [] полягає в тому, що E1 [E2] ідентичний (* ((E1) + (E2))). Через правила перетворення, що застосовуються до оператора бінарних +, якщо E1 є об'єктом масиву (еквівалентно, вказівник на початковий елемент об'єкта масиву), а E2 - ціле число, E1 [E2] позначає E2-й елемент E1 (рахуючи від нуля).
Vality

Щоб бути більш правильним: масиви розпадаються на покажчики, коли ви отримуєте доступ до них.
12431234123412341234123

Нітпік: Немає сенсу говорити, що " *(a + i)є комутативним". Однак, *(a + i) = *(i + a) = i[a]оскільки додавання є комутативним.
Андреас Рейбранд

231

Я думаю, що в інших відповідях щось не вистачає.

Так, p[i]за визначенням еквівалентний *(p+i), що (оскільки додавання є комутативним) еквівалентно *(i+p), що (знову ж таки, за визначенням []оператора) еквівалентно i[p].

(І в тому array[i], ім'я масиву неявно перетворюється на вказівник на перший елемент масиву.)

Але комутативність додавання не все так очевидно в даному випадку.

Коли обидва операнда мають значення того ж типу, або навіть різних числових типів, які сприяли до загального типу, коммутативности має сенс: x + y == y + x.

Але в цьому випадку ми говоримо конкретно про арифметику покажчика, де один операнд - покажчик, а інший - ціле число. (Ціле число + ціле число - це інша операція, а вказівник + покажчик - це нісенітниця.)

Опис +оператора стандарту C ( N1570 6.5.6) говорить:

Крім того, або обидва операнди мають арифметичний тип, або один операнд є вказівником на повний тип об'єкта, а інший має цілий тип.

Це так само легко могло сказати:

Крім того, або обидва операнди мають арифметичний тип, або лівий операнд є вказівником на повний тип об'єкта, а правий операнд має цілий тип.

в такому випадку і те, i + pі i[p]було б незаконно.

З точки зору C ++, у нас дійсно є два набори перевантажених +операторів, які можна вільно описати як:

pointer operator+(pointer p, integer i);

і

pointer operator+(integer i, pointer p);

з яких дійсно потрібен лише перший.

То чому ж так?

C ++ усвідомив це визначення від C, яке отримало його від B (комутативність індексації масивів чітко згадується у Посиланні користувачів 1972 року на B ), яка отримала його від BCPL (керівництво від 1967 р.), Яке , можливо, отримало його навіть від більш ранні мови (CPL? Algol?).

Отже, ідея про те, що індексація масиву визначена в термінах додавання, і що додавання, навіть вказівник і ціле число, є комутативним, йде багато десятиліть на мови предків С.

Ці мови були набагато менш сильно набрані, ніж сучасна мова С. Зокрема, різницю між вказівниками та цілими числами часто ігнорували. (Програмісти раннього С іноді використовували вказівники як цілі числа без підпису, перш ніж unsignedключове слово було додано до мови.) Отже, ідея зробити додавання некомутативним, оскільки операнди різного типу, ймовірно, не виникли б у дизайнерів цих мов. Якщо користувач хотів додати дві "речі", будь то ці "цілі", цілі числа, покажчики чи щось інше, мова не могла запобігти.

І з роками будь-яка зміна цього правила порушила б існуючий код (хоча стандарт ANSI C 1989 р. Міг бути гарною можливістю).

Змінюючи C та / або C ++, щоб вимагати введення вказівника зліва та цілого числа справа може порушити деякий існуючий код, але реальної виразної сили не буде втрачено.

Отже, ми маємо arr[3]і маємо на 3[arr]увазі саме те саме, хоча остання форма ніколи не повинна з’являтися поза МОККЦ .


12
Фантастичний опис цього об'єкту. З точки зору високого рівня, я думаю 3[arr], що це цікавий артефакт, але його слід рідко використовувати, якщо взагалі використовувати. Прийнята відповідь на це запитання (< stackoverflow.com/q/1390365/356> ), яку я запитала назад, змінила те, як я думала про синтаксис. Хоча технічно часто це не є правильним і неправильним способом зробити ці речі, такі особливості примушують вас думати так, як це є окремим від деталей реалізації. Користь має такий різний спосіб мислення, який частково втрачається, коли ви зациклюєтесь на деталях реалізації.
Діна

3
Додавання є комутативним. Для стандарту С визначати це інакше було б дивно. Ось чому це було не так просто сказано: "Крім того, або обидва операнди мають арифметичний тип, або лівий операнд буде вказівником на повний тип об'єкта, а правий операнд має цілий тип". - Це не було б сенсу для більшості людей, які додають речі.
iheanyi

9
@iheanyi: Додавання зазвичай комутативне - воно зазвичай займає два операнди одного типу. Додавання вказівника дозволяє додавати вказівник та ціле число, але не два покажчики. ІМХО, це вже досить дивний особливий випадок, що вимагати, щоб вказівник був лівим операндом, не було б суттєвим тягарем. (Деякі мови використовують "+" для конкатенації рядків; це, звичайно, не комутативно.)
Кіт Томпсон,

3
@supercat, Це ще гірше. Це означало б, що іноді x + 1! = 1 + x. Це повністю порушило б асоціативну властивість додавання.
iheanyi

3
@iheanyi: Я думаю, ви мали на увазі комутативну властивість; додавання вже не асоціативне, оскільки для більшості реалізацій (1LL + 1U) -2! = 1LL + (1U-2). Дійсно, зміна зробить деякі ситуації асоціативними, яких зараз немає, наприклад 3U + (UINT_MAX-2L) було б рівним (3U + UINT_MAX) -2. Що найкраще, однак, щоб мова мала додавати нові чіткі типи для рекламних цілих чисел та "обгортання" алгебраїчних кілець, так що додавання 2 до а, ring16_tщо містить 65535, дало б ring16_tзначення зі значенням 1, незалежно від розміруint .
supercat

196

І звичайно

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

Основною причиною цього було те, що ще в 70-х, коли проектувався C, комп'ютери не мали багато пам'яті (64 КБ було багато), тому компілятор C не робив багато перевірки синтаксису. Отже, " X[Y]" було досить сліпо переведено на " *(X+Y)"

Це також пояснює синтаксиси " +=" та " ++". Усе у формі " A = B + C" мала однакову складену форму. Але якщо B був таким самим об'єктом, що і A, тоді була доступна оптимізація рівня складання. Але компілятор був недостатньо яскравим, щоб його розпізнати, тому розробнику довелося ( A += C). Так само, якщо це Cбуло 1, була доступна інша оптимізація рівня складання, і знову розробнику довелося зробити це явним, оскільки компілятор не розпізнав його. (З недавніх пір компілятори це роблять, тому ці синтаксиси в ці дні є непотрібними)


127
Власне, це оцінюється як хибне; перший термін "ABCD" [2] == 2 ["ABCD"] оцінює як істинне, або 1, і 1! = "C": D
Джонатан Леффлер

8
@Jonathan: така ж неоднозначність призводить до редагування оригінальної назви цієї публікації. Чи є ми рівними знаками математичної еквівалентності, синтаксису коду чи псевдокоду. Я заперечую математичну рівноправність, але оскільки ми говоримо про код, ми не можемо уникнути того, що ми переглядаємо все з точки зору синтаксису коду.
Діна

19
Це не міф? Я маю на увазі, що оператори + = і ++ були створені для спрощення компілятора? Деякий код стає зрозумілішим з ними, і корисним є синтаксис, незалежно від того, що з ним робить компілятор.
Томас Падрон-Маккарті

6
+ = і ++ має ще одну істотну перевагу. якщо під час оцінки ліва сторона змінить якусь змінну, зміна буде здійснена лише один раз. a = a + ...; зробить це двічі.
Йоханнес Шауб - ліб

8
Ні - "ABCD" [2] == * ("ABCD" + 2) = * ("CD") = 'C'. Перенаправлення рядка дає
точку

55

Одне, здається, ніхто не згадував про проблему Діни з sizeof:

Ви можете додати лише ціле число до вказівника, ви не можете додати два покажчики разом. Таким чином, додаючи покажчик на ціле число або ціле число на покажчик, компілятор завжди знає, який біт має розмір, який потрібно враховувати.


1
Про це є досить вичерпна розмова в коментарях до прийнятої відповіді. Я вказав розмову в редакції на початкове запитання, але безпосередньо не торкнувся вашої дуже поважної проблеми щодо sizeof. Не знаєте, як найкраще це зробити в ТА. Чи варто зробити ще одну редакцію оригіналу питання?
Діна

50

Щоб відповісти на питання буквально. Це не завжди такx == x

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

відбитки

false

27
Насправді «нянь» не дорівнює собі: cout << (a[5] == a[5] ? "true" : "false") << endl;є false.
TrueY

8
@TrueY: Він заявив, що спеціально для випадку NaN (а конкретно, x == xце не завжди так). Я думаю, що це був його намір. Так він технічно правильний (і, можливо, як кажуть, найкращий вид правильний!).
Тім Час

3
Питання стосується C, ваш код - не код C. Є також NANin <math.h>, що краще, ніж 0.0/0.0, тому що 0.0/0.0це UB, коли __STDC_IEC_559__він не визначений (більшість реалізацій не визначають __STDC_IEC_559__, але в більшості реалізацій 0.0/0.0все ще працюватимуть)
12431234123412341234123

26

Я просто з'ясував, що цей потворний синтаксис може бути "корисним" або, принаймні, дуже цікавим, з яким грати, коли ви хочете мати справу з масивом індексів, які посилаються на позиції в одному масиві. Він може замінити вкладені квадратні дужки та зробити код більш читабельним!

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)

}

Звичайно, я цілком впевнений, що для цього немає справжнього випадку в реальному коді, але мені все одно було цікаво :)


Коли ви бачите, i[a][a][a]ви думаєте, що я або вказівник на масив, або масив вказівника на масив або масив ... і aє індексом. Коли ви бачите a[a[a[i]]], ви думаєте, що a - це вказівник на масив чи масив і iє індексом.
12431234123412341234123

1
Оце Так! Дуже круто використання цієї "дурної" функції. Може бути корисним в алгоритмічному змаганні в деяких проблемах))
Серж Бреусов,

26

Приємне запитання / відповіді.

Просто хочу зазначити, що покажчики та масиви C не є однаковими , хоча в цьому випадку різниця не є істотною.

Розглянемо наступні декларації:

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

У a.out, символ aзнаходиться за адресою, яка є початком масиву, а символ p- за адресою, де зберігається покажчик, а значення вказівника в цьому місці пам'яті є початком масиву.


2
Ні, технічно вони не однакові. Якщо ви визначите деякий b як int * const і примусите його вказувати на масив, це все ще вказівник, тобто в таблиці символів b посилається на місце пам'яті, яке зберігає адресу, що, в свою чергу, вказує на те, де знаходиться масив .
PolyThinker

4
Дуже хороший момент. Я пам’ятаю, що мав дуже неприємну помилку, коли я визначив глобальний символ як char s [100] в одному модулі, оголосив його як зовнішній char * s; в іншому модулі. Після з'єднання все це програма поводилася дуже дивно. Оскільки модуль, що використовує зовнішнє оголошення, використовував початкові байти масиву як вказівник на char.
Джорджо

1
Спочатку в бабусі-дідусі BCPL масив був вказівником. Тобто, те, що ви отримали, коли ви писали (я транслітерував на C), int a[10]- це вказівник під назвою "a", який вказував на достатнє сховище для 10 цілих чисел в інших місцях. Таким чином, + i i j + i мали однакову форму: додайте вміст пари місць розташування пам'яті. Насправді, я думаю, що BCPL був безтиповим, тому вони були ідентичними. І масштабування розміру типу не застосовувалося, оскільки BCPL суто орієнтований на слова (також на машинах, адресованих словом).
Дейв

Я думаю, що найкращий спосіб зрозуміти різницю - це порівняти int*p = a;з int b = 5; В останньому, "b" і "5" є обома цілими числами, але "b" є змінною, тоді як "5" - це фіксоване значення. Аналогічно, "p" & "a" є обома адресами символу, але "a" є фіксованим значенням.
Джеймс Курран


15

Не відповідь, а просто трохи їжі для роздумів. Якщо у класу перевантажений оператор індексу / підписки, вираз 0[x]не буде працювати:

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

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

Оскільки у нас немає доступу до класу int , цього неможливо зробити:

class int
{
   int operator[](const Sub&);
};

2
class Sub { public: int operator[](size_t nIndex) const { return 0; } friend int operator[](size_t nIndex, const Sub& This) { return 0; } };
Ben Voigt

1
Ви насправді спробували її скласти? Є безліч операторів, які неможливо реалізувати поза класом (тобто як нестатичні функції)!
Аджай

3
ой, ти маєш рацію. " operator[]повинна бути нестатичною функцією члена з точно одним параметром." Я був знайомий з цим обмеженням operator=, не вважав, що воно стосується [].
Ben Voigt

1
Звичайно, якщо ви зміните визначення []оператора, воно більше ніколи не було б еквівалентним ... якщо a[b]це дорівнює, *(a + b)і ви зміните це, вам доведеться також перевантажувати int::operator[](const Sub&);і intце не клас ...
Луїс Колорадо,

7
Це ... не ... С.
MD XF

11

Це дуже вдале пояснення у навчальному посібнику з покажчиків та масивів на C від Теда Дженсена.

Тед Дженсен пояснив це так:

Насправді це правда, тобто там, де хто пише, a[i]його можна замінити *(a + i) без проблем. Фактично компілятор створить один і той же код в будь-якому випадку. Таким чином, ми бачимо, що арифметика вказівника - це те саме, що і індексація масиву. Будь-який синтаксис дає однаковий результат.

Це НЕ говорить про те, що вказівники та масиви - це одне і те ж, це не так. Ми лише говоримо, що для ідентифікації заданого елемента масиву у нас є вибір двох синтаксисів, один з використанням індексації масиву, а другий з використанням арифметики вказівника, які дають однакові результати.

Тепер, дивлячись на цей останній вираз, його частину .. (a + i), є простим доповненням, що використовує оператор +, а правила C заявляють, що такий вираз є комутативним. Тобто (a + i) тотожне (i + a). Таким чином, ми могли писати *(i + a)так само легко *(a + i). Але *(i + a)міг би взятися i[a]! З усього цього випливає цікава правда, що якщо:

char a[20];

написання

a[3] = 'x';

те саме, що писати

3[a] = 'x';

4
a + i НЕ є простим доповненням, оскільки це арифметика вказівника. якщо розмір елемента a дорівнює 1 (char), то так, це так само, як ціле +. Але якщо це (наприклад) ціле число, то воно може бути еквівалентним а + 4 * i.
Алекс Браун

@AlexBrown Так, це арифметика вказівника, саме тому ваше останнє речення є неправильним, якщо ви спочатку не подали "a", щоб бути (char *) (якщо вважати, що int - 4 знаки). Я дійсно не розумію, чому стільки людей зациклюються на фактичному результаті арифметики вказівника. Основна мета арифметики вказівника полягає в тому, щоб абстрагувати основні значення вказівника і дозволити програмісту думати про об'єкти, якими маніпулює, а не над значеннями адреси.
jschultz410

8

Я знаю, що на це питання відповіли, але я не втримався поділитися цим поясненням.

Я пам’ятаю Принципи дизайну компілятора, припустимо a, це intмасив і розмір int- 2 байти, а базова адреса a- 1000.

Як a[5]буде працювати ->

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

Тому,

Аналогічно, коли код c розбивається на 3-адресний код, 5[a]стане ->

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

Таким чином , в основному обидві заяви вказують на те ж місце в пам'яті і , отже, a[5] = 5[a].

Це пояснення також є причиною того, що негативні показники в масивах працюють у C.

тобто якщо я отримаю доступ, a[-5]це дасть мені

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

Він поверне мені об’єкт за адресою 990.


6

У масивах С , arr[3]і 3[arr]одні і ті ж, і їх еквівалентні позначення покажчиків є *(arr + 3)для *(3 + arr). Але навпаки [arr]3або [3]arrне є правильним і призведе до синтаксичної помилки, як (arr + 3)*і (3 + arr)*не є дійсними виразами. Причина полягає в тому, що оператор перенаправлення повинен бути розміщений перед адресою, отриманою виразу, а не після адреси.


6

в c компілятор

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

є різні способи посилання на елемент у масиві! (НЕ ВСЕ ВЕЛИКИЙ)


5

Трохи історії зараз. Серед інших мов BCPL мала досить великий вплив на ранній розвиток С. Якщо ви оголосили масив у BCPL з чимось на зразок:

let V = vec 10

що фактично виділило 11 слів пам’яті, а не 10. Як правило, V було першим і містило адресу одразу наступного слова. Тож на відміну від C, іменуючи V, перейшов до цього місця і підхопив адресу нульового елемента масиву. Тому непряме значення масиву в BCPL виражається як

let J = V!5

насправді доводилося робити J = !(V + 5)(використовуючи синтаксис BCPL), оскільки потрібно було отримати V, щоб отримати базову адресу масиву. Таким чином V!5і 5!Vбули синонімами. Як анекдотичне спостереження, WAFL (функціональна мова Warwick) була написана в BCPL, і на мій об'єм пам'яті, як правило, використовував останній синтаксис, а не перший для доступу до вузлів, що використовуються для зберігання даних. Зрозуміло, це десь від 35 до 40 років тому, тому моя пам’ять трохи іржава. :)

Інновація розповсюдження додаткового слова зберігання та надання компілятору вставити базову адресу масиву, коли він був названий, з’явився пізніше. Згідно з документом історії C, це сталося приблизно тоді, коли структури були додані до C.

Зауважте, що !в BCPL був і оператор унарного префікса, і оператор бінарної інфіксації, в обох випадках робили непряму. лише те, що двійкова форма включала додавання двох операндів перед тим, як здійснити непряму. Зважаючи на характер, орієнтований на слова BCPL (та B), це насправді мало сенс. Обмеження "покажчик і ціле число" стало необхідним у С, коли він набирав типи даних, і sizeofстав річчю.


1

Ну, це особливість, яка можлива лише через мовну підтримку.

Компілятор інтерпретує a[i]як *(a+i)і вираз, який 5[a]оцінює *(5+a). Оскільки додавання є комутативним, виходить, що обидва рівні. Звідси вираз оцінюється на true.


Хоча надмірне, це чітко, стисло та коротко.
Білл К

0

В С

 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

Покажчик - це "змінна"

Назва масиву - "мнемонічний" або "синонім"

p++;дійсний, але a++недійсний

a[2] дорівнює 2 [a], тому що внутрішня операція на обох це

"Арифметика вказівника" внутрішньо розраховується як

*(a+3) дорівнює *(3+a)


-4

типи вказівників

1) вказівник на дані

int *ptr;

2) const вказівник на дані

int const *ptr;

3) покажчик const на дані const

int const *const ptr;

і масиви є типом (2) з нашого списку.
Коли ви визначаєте масив одночасно, одна адреса ініціалізується в цьому покажчику.
Як ми знаємо, що ми не можемо змінити або змінити значення const у нашій програмі, тому що він кидає помилку при компіляції час

Основна відмінність я знайшов ...

Ми можемо повторно ініціалізувати покажчик за адресою, але не той самий випадок із масивом.

======
і повернутися до вашого питання ...
a[5]це нічого, але *(a + 5)
ви можете легко зрозуміти,
a що містить адресу (люди називають її базовою адресою) так само, як (2) тип вказівника у нашому списку
[]- цим оператором може бути замінюється покажчиком *.

так нарешті ...

a[5] == *(a +5) == *(5 + a) == 5[a] 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.