При изучении C (или C++, или и того, и другого), что, давайте будем правдой, все меньше и меньше людей делают в наши дни, требуется некоторое время, чтобы понять указатели или даже просто изучить синтаксис указателей, разыменование, многомерные массивы и, конечно же, — указатели на функции. Некоторым из них требуется время, чтобы привыкнуть и распознать, что происходит при чтении чужого кода. Давайте освежим в памяти некоторые основы работы с массивами, прежде чем я объясню один странный прием, который на первый взгляд удивителен, но в то же время вполне логичен.
Итак, массив можно объявить по-разному, например int a[10];
— это массив из десяти целых чисел. Синтаксис массива приятен и прост для понимания и чтения, но когда вы начинаете делать что-то реальное, включающее более сложные типы или многомерные массивы, становится проще просто рассматривать ваши массивы как блоки памяти с указателем на первый элемент. Нет другого способа создать массив неизвестного во время компиляции, кроме объявления указателя и выделения памяти вручную.
Если вы объявите указатель: int *pa;
, то вы можете присвоить ему адрес первого элемента в a
следующим образом: pa = &a[0];
. Теперь, используя арифметику указателей, легко получить доступ ко всем остальным элементам вашего массива: например, *(pa+1)
указывает на второй элемент, а присваивание y = *(pa+1)
присваивает значение второго элемента int
переменной y
. Конечно, *(pa+i)
дает вам доступ к любому элементу и совпадает с a[i]
(только будьте осторожны, чтобы не выйти за пределы выделенной памяти).
И вот, наконец, неожиданный трюк. C всегда преобразует синтаксис вашего массива a[i]
в *(a+i)
, и при этом ему не важно, какая из a
и i
является переменной массива, а какая — индексом. Таким образом, i[a]
работает так же, как a[i]
, и работает с пронумерованными индексами, поэтому a[3]
совпадает с 3[a]
и дает вам четвертый элемент массива.
Эту небольшую статью я хочу посвятить покойному Деннису Ритчи, смерть которого в далеком 2011 году прошла практически незамеченной.