Язык С

* Указатели и массивы *


Указатель - это переменная, содержащая адрес другой пе- ременной. указатели очень широко используются в языке "C". Это происходит отчасти потому, что иногда они дают единст- венную возможность выразить нужное действие, а отчасти пото- му, что они обычно ведут к более компактным и эффективным программам, чем те, которые могут быть получены другими спо- собами. Указатели обычно смешивают в одну кучу с операторами GOTO, характеризуя их как чудесный способ написания прог- рамм, которые невозможно понять. Это безусловно спрAведливо, если указатели используются беззаботно; очень просто ввести указатели, которые указывают на что-то совершенно неожидан- ное. Однако, при определенной дисциплине, использование ука- зателей помогает достичь ясности и простоты. Именно этот ас- пект мы попытаемся здесь проиллюстрировать.


    PA = A

    Еще более удивительным, по крайней мере на первый взг- ляд, кажется тот факт, что ссылку на A[I] можно записать в виде *(A+I). При анализировании выражения A[I] в языке "C" оно немедленно преобразуется к виду *(A+I); эти две формы совершенно эквивалентны. Если применить операцию & к обеим частям такого соотношения эквивалентности, то мы получим, что &A[I] и A+I тоже идентичны: A+I - адрес I-го элемента от начала A. С другой стороны, если PA является указателем, то в выражениях его можно использовать с индексом: PA[I] иден- тично *(PA+I). Короче, любое выражение, включающее массивы и индексы, может быть записано через указатели и смещения и наоборот, причем даже в одном и том же утверждении.

    Имеется одно различие между именем массива и указателем, которое необходимо иметь в виду. указатель является перемен- ной, так что операции PA=A и PA++ имеют смысл. Но имя масси- ва является константой, а не переменной: конструкции типа A=PA или A++,или P=&A будут незаконными. Когда имя массива передается функции, то на самом деле ей передается местоположение начала этого массива. Внутри вызванной функции такой аргумент является точно такой же пе- ременной, как и любая другая, так что имя массива в качестве аргумента действительно является указателем, т.е. Перемен- ной, содержащей адрес. мы можем использовать это обстоятель- ство для написания нового варианта функции STRLEN, вычисляю- щей длину строки.

    STRLEN(S) /* RETURN LENGTH OF STRING S */ CHAR *S; { INT N;

    FOR (N = 0; *S != '\0'; S++) N++; RETURN(N); }

    Операция увеличения S совершенно законна, поскольку эта переменная является указателем; S++ никак не влияет на сим- вольную строку в обратившейся к STRLEN функции, а только увеличивает локальную для функции STRLEN копию адреса. Опи- сания формальных параметров в определении функции в виде

    CHAR S[]; CHAR *S;

    совершенно эквивалентны; какой вид описания следует предпо- честь, определяется в значительной степени тем, какие выра- жения будут использованы при написании функции. Если функции передается имя массива, то в зависимости от того, что удоб- нее, можно полагать, что функция оперирует либо с массивом, либо с указателем, и действовать далее соответвующим обра- зом. Можно даже использовать оба вида операций, если это ка- жется уместным и ясным. Можно передать функции часть массива, если задать в ка- честве аргумента указатель начала подмассива. Например, если A - массив, то как

    F(&A[2])

    как и

    F(A+2)

    передают функции F адрес элемента A[2], потому что и &A[2], и A+2 являются указательными выражениями, ссылающимися на третий элемент A. внутри функции F описания аргументов могут присутствовать в виде:

    F(ARR) INT ARR[]; { ... }

    или

    F(ARR) INT *ARR; { ... }

    Что касается функции F, то тот факт, что ее аргумент в дейс- твительности ссылается к части большего массива,не имеет для нее никаких последствий.




      Содержание раздела