Второе издание, стр. 104:
5.5. Символьные указатели и функции
A строковая константа , записанная как
"I am a string"
это массив символов. Во внутреннем представлении массив заканчивается нулевым символом '\0'
, чтобы программы могли найти его конец. Таким образом, длина в хранилище на один больше числа символов в двойных кавычках.
Возможно, самое распространенное вхождение строковых констант - это аргументы функций, как в
printf("hello, world\n");
Если в программе появляется такая символьная строка, доступ к ней осуществляется через символьный указатель; printf
получает указатель на начало массива символов. То есть строковая константа доступна указателем на ее первый элемент.
Строковые константы не обязательно должны быть аргументами функций. Если pmessage
объявлено как
char *pmessage;
тогда утверждение
pmessage = "now is the time";
присваивает pmessage
указатель на массив символов. Это не строковая копия; участвуют только указатели. C не предоставляет никаких операторов для обработки всей строки символов как единого целого.
Существует важное различие между этими определениями:
char amessage[] = "now is the time"; /* an array */
char *pmessage = "now is the time"; /* a pointer */
amessage
- это массив, достаточно большой, чтобы содержать последовательность символов и '\0'
, которая его инициализирует. Отдельные символы в массиве могут быть изменены на amessage
, всегда ссылаясь на одно и то же хранилище. С другой стороны, pmessage
- указатель, инициализированный для указания на строковую константу; впоследствии указатель может быть изменен, чтобы указывать в другом месте, но результат будет неопределенным, если вы попытаетесь изменить содержимое строки.
+---+ +--------------------+
pmessage: | o-------->| now is the time \0 |
+---+ +--------------------+
+--------------------+
amessage: | now is the time \0 |
+--------------------+
Мы проиллюстрируем больше аспектов указателей и массивов, изучая версии двух полезных функций, адаптированных из стандартной библиотеки. Первая функция - strcpy(s,t)
, которая копирует строку t
в строку s
. Было бы неплохо просто сказать s = t
, но это копирует указатель, а не символы. Чтобы скопировать символы, нам нужен цикл. Версия массива первая:
/* strcpy: copy t to s; array subscript version */
void strcpy(char *s, char *t)
{
int i;
i = 0;
while((s[i] = t[i]) != '\0')
i ++;
}
Для контраста вот версия strcpy
с указателями:
/* strcpy: copy t to s; pointer version 1 */
void strcpy(char *s, char *t)
{
while((*s = *t) != '\0')
{
s ++;
t ++;
}
}
Поскольку аргументы передаются по значению, strcpy
может использовать параметры s
и t
любым удобным для него способом. Здесь они представляют собой удобно инициализированные указатели, которые маршируют по массивам персонажа за раз, пока '\0'
, заканчивающийся t
, не будет скопирован в s
.
На практике strcpy
не будет написано, как мы показали выше. Опытные программисты на Си предпочли бы
/* strcpy: copy t to s; pointer version 2 */
void strcpy(char *s, char *t)
{
while((*s++ = *t++) != '\0')
;
}
Это перемещает приращение s
и t
в тестовую часть цикла. Значение *t++
- это символ, на который t
указывал до увеличения t
; постфикс ++
не изменяется t
до тех пор, пока не будет выбран этот символ. Таким же образом, символ сохраняется в старой позиции s
до увеличения s
. Этот символ также является значением, которое сравнивается с '\0'
для управления циклом. Чистый эффект заключается в том, что символы копируются с t
до s
, вплоть до завершающего '\0'
.
В качестве последней аббревиатуры, обратите внимание, что сравнение с '\0'
является излишним, поскольку вопрос заключается лишь в том, является ли выражение нулевым. Так что функция, скорее всего, будет записана как
/* strcpy: cope t to s; pointer version 3 */
void strcpy(char *s, char *t)
{
while(*s++ = *t++);
}
Хотя это может показаться загадочным на первый взгляд, удобство обозначений значительно, и идиома должна быть освоена, потому что вы будете часто видеть это в программах на Си.
strcpy
в стандартной библиотеке (<string.h>
) возвращает целевую строку в качестве значения функции.
Это конец соответствующих частей этого раздела.
PS: Если вам понравилось это чтение, подумайте о покупке копии K & R - это не дорого.