Опубликован: 05.07.2006 | Доступ: свободный | Студентов: 4679 / 885 | Оценка: 4.12 / 3.74 | Длительность: 18:59:00
Лекция 6:

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

5.5. Указатели символов и функции

Строчная константа, как, например,

"i am a string"

является массивом символов. Компилятор завершает внутреннее представление такого массива символом \0, так что программы могут находить его конец. Таким образом, длина массива в памяти оказывается на единицу больше числа символов между двойными кавычками.

По-видимому чаще всего строчные константы появляются в качестве аргументов функций, как, например, в

printf ("hello, world\n");

когда символьная строка, подобная этой, появляется в программе, то доступ к ней осуществляется с помощью указателя символов; функция printf фактически получает указатель символьного массива.

Конечно, символьные массивы не обязаны быть только аргументами функций. Если описать message как

char *message;

то в результате оператора

message = "now is the time";

переменная message станет указателем на фактический массив символов. Это не копирование строки; здесь участвуют только указатели. В языке "C" не предусмотрены какие-либо операции для обработки всей строки символов как целого.

Мы проиллюстрируем другие аспекты указателей и массивов, разбирая две полезные функции из стандартной библиотеки ввода-вывода, которая будет рассмотрена в "лекции №7" .

Первая функция - это strcpy(s,t), которая копирует строку t в строку s. аргументы написаны именно в этом порядке по аналогии с операцией присваивания, когда для того, чтобы присвоить t к s обычно пишут

s = t

сначала приведем версию с массивами:

strcpy(s, t)    /* copy t to s */
char s[], t[];
{
   int i;
   i = 0;
   while ((s[i] = t[i]) != '\0')
           i++;
}

Для сопоставления ниже дается вариант strcpy с указателями.

strcpy(s, t)  /* copy t to s; pointer version 1 */
char *s, *t;
{
   while ((*s = *t) != '\0') {
           s++;
           t++;
   }
}

Так как аргументы передаются по значению, функция strcpy может использовать s и t так, как она пожелает. Здесь они с удобством полагаются указателями, которые передвигаются вдоль массивов, по одному символу за шаг, пока не будет скопирован в s завершающий в t символ \0.

На практике функция strcpy была бы записана не так, как мы показали выше. Вот вторая возможность:

strcpy(s, t)  /* copy t to s; pointer version 2 */
char *s, *t;
{
   while ((*s++ = *t++) != '\0')
           ;
}

Здесь увеличение s и t внесено в проверочную часть. Значением *t++ является символ, на который указывал t до увеличения; постфиксная операция ++ не изменяет t, пока этот символ не будет извлечен. Точно так же этот символ помещается в старую позицию s, до того как s будет увеличено. Конечный результат заключается в том, что все символы, включая завершающий \0, копируются из t в s.

И как последнее сокращение мы опять отметим, что сравнение с \0 является излишним, так что функцию можно записать в виде

strcpy(s, t) /* copy t to s; pointer version 3 */ 
char *s, *t; 
{ 
      while (*s++ = *t++) 
              ; 
}

хотя с первого взгляда эта запись может показаться загадочной, она дает значительное удобство. Этой идиомой следует овладеть уже хотя бы потому, что вы с ней будете часто встречаться в "C"- программах.

Вторая функция - strcmp(s, t), которая сравнивает символьные строки s и t, возвращая отрицательное, нулевое или положительное значение в соответствии с тем, меньше, равно или больше лексикографически s, чем t. Возвращаемое значение получается в результате вычитания символов из первой позиции, в которой s и t не совпадают.

strcmp(s, t) /* return <0 if s<t, 0 if s==t, >0 if s>t */
char s[], t[];
{
 int i;

 i = 0;
 while (s[i] == t[i])
    if (s[i++] == '\0')
            return(0);
 return(s[i]-t[i]);
}

Вот версия strcmp с указателями:

strcmp(s, t) /* return <0 if s<t, 0 if s==t, >0 if s>t */
char *s, *t;
{
 for ( ; *s == *t; s++, t++)
    if (*s == '\0')
            return(0);
 return(*s-*t);
}

так как ++ и -- могут быть как постфиксными, так и префиксными операциями, встречаются другие комбинации * и ++ и --, хотя и менее часто.

Например

*++p

увеличивает p до извлечения символа, на который указывает p, а

*--p

сначала уменьшает p.

Упражнение 5-2

Напишите вариант с указателями функции strcat из "лекции №2" : strcat(s, t) копирует строку t в конец s.

Упражнение 5-3

Напишите макрос для strcpy.

Упражнение 5-4

Перепишите подходящие программы из предыдущих лекций и упражнений, используя указатели вместо индексации массивов. Хорошие возможности для этого предоставляют функции getline / "лекции 1" и "№4" /, atoi, itoa и их варианты / "лекция №2" , "№3" и "№4" /, reverse / "лекция №3" /, index и getop / "лекция №4" /.