Во-вторых, как вы уже, наверное, заметили, указатели и целые можно складывать и вычитать. Конструкция
p + n
означает адрес объекта, занимающего n -е место после объекта, на который указывает p . Это справедливо безотносительно к типу объекта, на который указывает p ; n автоматически домножается на коэффициент, соответствующий размеру объекта. Информация о размере неявно присутствует в объявлении p . Если, к примеру, int занимает четыре байта, то коэффициент умножения будет равен четырем.
Допускается также вычитание указателей. Например, если p и q указывают на элементы одного массива и p‹q , то q-p+1 есть число элементов от p до q включительно. Этим фактом можно воспользоваться при написании еще одной версии strlen :
/* strlen: возвращает длину строки s */
int strlen(char *s)
{
char *p = s;
while (*p != '\0')
p++;
return p - s;
}
В своем объявлении p инициализируется значением s , т. е. вначале p указывает на первый символ строки. На каждом шаге цикла while проверяется очередной символ; цикл продолжается до тех пор, пока не встретится '\0'. Каждое продвижение указателя p на следующий символ выполняется инструкцией p++ , и разность p-s дает число пройденных символов, т. е. длину строки. (Число символов в строке может быть слишком большим, чтобы хранить его в переменной типа int . Тип ptrdiff_t, достаточный для хранения разности (со знаком) двух указателей, определен в заголовочном файле ‹stddef.h›. Однако, если быть очень осторожными, нам следовало бы для возвращаемого результата использовать тип size_t , в этом случае наша программа соответствовала бы стандартной библиотечной версии. Тип size_tесть тип беззнакового целого, возвращаемого оператором sizeof.
Арифметика с указателями учитывает тип: если она имеет дело со значениями float , занимающими больше памяти, чем char , и p - указатель на float , то p++ продвинет p на следующее значение float . Это значит, что другую версию alloc , которая имеет дело с элементами типа float , а не char , можно получить простой заменой в alloc и afree всех char на float . Все операции с указателями будут автоматически откорректированы в соответствии с размером объектов, на которые указывают указатели.
Можно производить следующие операции с указателями: присваивание значения указателя другому указателю того же типа, сложение и вычитание указателя и целого, вычитание и сравнение двух указателей, указывающих на элементы одного и того же массива, а также присваивание указателю нуля и сравнение указателя с нулем. Других операций с указателями производить не допускается. Нельзя складывать два указателя, перемножать их, делить, сдвигать, выделять разряды; указатель нельзя складывать со значением типа float или double ; указателю одного типа нельзя даже присвоить указатель другого типа, не выполнив предварительно операции приведения (исключение составляют лишь указатели типа void*).
5.5 Символьные указатели функции
Строковая константа , написанная в виде
"Я строка"
есть массив символов. Во внутреннем представлении этот массив заканчивается нулевым символом '\0', по которому программа может найти конец строки. Число занятых ячеек памяти на одну больше, чем количество символов, помещенных между двойными кавычками.
Чаще всего строковые константы используются в качестве аргументов функций, как, например, в
printf("здравствуй, мир\n");
Когда такая символьная строка появляется в программе, доступ к ней осуществляется через символьный указатель; printf получает указатель на начало массива символов. Точнее, доступ к строковой константе осуществляется через указатель на ее первый элемент.
Строковые константы нужны не только в качестве аргументов функций. Если, например, переменную pmessage объявить как
char *pmessage;
то присваивание
pmessage = "now is the time";
поместит в нее указатель на символьный массив, при этом сама строка не копируется, копируется лишь указатель на нее. Операции для работы со строкой как с единым целым в Си не предусмотрены.
Существует важное различие между следующими определениями:
char amessage[] = "now is the time"; /* массив */
char *pmessage = "now is the time"; /* указатель */
amessage - это массив, имеющий такой объем, что в нем как раз помещается указанная последовательность символов и '\0'. Отдельные символы внутри массива могут изменяться, но amessage всегда указывает на одно и то же место памяти. В противоположность ему pmessage есть указатель, инициализированный так, чтобы указывать на строковую константу. А значение указателя можно изменить, и тогда последний будет указывать на что-либо другое. Кроме того, результат будет неопределен, если вы попытаетесь изменить содержимое константы.
Читать дальше