Категории
Самые читаемые
ChitatKnigi.com » 🟢Компьютеры и Интернет » Программирование » Язык программирования Си. Издание 3-е, исправленное - Брайан Керниган

Язык программирования Си. Издание 3-е, исправленное - Брайан Керниган

Читать онлайн Язык программирования Си. Издание 3-е, исправленное - Брайан Керниган
1 ... 28 29 30 31 32 33 34 35 36 ... 81
Перейти на страницу:

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать

int *daytab[13]

определяет массив из 13 указателей на char. В более общем случае только первое измерение (соответствующее первому индексу) можно не задавать, все другие специфицировать необходимо. В параграфе 5.12 мы продолжим рассмотрение сложных объявлений.

Упражнение 5.8. В функциях day_of_year и month_day нет никаких проверок правильности вводимых дат. Устраните этот недостаток.

5.8 Инициализация массивов указателей

Напишем функцию month_name(n), которая возвращает указатель на строку символов, содержащий название n-го месяца. Эта функция идеальна для демонстрации использования статического массива. Функция month_name имеет в своем личном распоряжении массив строк, на одну из которых она и возвращает указатель. Ниже покажем, как инициализируется этот массив имен.

Синтаксис задания начальных значений аналогичен синтаксису предыдущих инициализаций:

/* month_name: возвращает имя n-го месяца */

char *month_name(int n)

{

 static char *name[] = {

  "Неверный месяц",

  "Январь","Февраль","Март","Апрель","Май","Июнь",

  "Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"

 };

 return (n ‹ 1 || n › 12) ? name[0] : name[n];

}

Объявление name массивом указателей на символы такое же, как и объявление lineptr в программе сортировки. Инициализатором служит список строк, каждой из которых соответствует определенное место в массиве. Символы i-й строки где-то размещены, и указатель на них запоминается в name[i]. Так как размер массива name не специфицирован, компилятор вычислит его по количеству заданных начальных значений.

5.9 Указатели против многомерных массивов

Начинающие программировать на Си иногда не понимают, в чем разница между двумерным массивом и массивом указателей вроде name из приведенного примера. Для двух следующих определений:

int a[10][20];

int *b[10];

записи a[3][4] и b[3][4] будут синтаксически правильным обращением к некоторому значению типа int. Однако только a является истинно двумерным массивом: для двухсот элементов типа int будет выделена память, а вычисление смещения элемента a[строка][столбец] от начала массива будет вестись по формуле 20 * строка + столбец, учитывающей его прямоугольную природу. Для b же определено только 10 указателей, причем без инициализации. Инициализация должна задаваться явно -либо статически, либо в программе. Предположим, что каждый элемент b указывает на двадцатиэлементный массив, в результате где-то будут выделены пространство, в котором разместятся 200 значений типа int, и еще 10 ячеек для указателей. Важное преимущество массива указателей в том, что строки такого массива могут иметь разные длины. Таким образом, каждый элемент массива b не обязательно указывает на двадцатиэлементный вектор; один может указывать на два элемента, другой - на пятьдесят, а некоторые и вовсе могут ни на что не указывать.

Наши рассуждения здесь касались целых значений, однако чаще массивы указателей используются для работы со строками символов, различающимися по длине, как это было в функции month_name. Сравните определение массива указателей и соответствующий ему рисунок:

char *name[] = {"Неправильный месяц", "Янв", "Февр", "Март"};

с объявлением и рисунком для двумерного массива:

char aname[][15] = {"Неправ. месяц", "Янв", "Февр", "Март"};

Упражнение 5.9. Перепишите программы day_of_year и month_day, используя вместо индексов указатели.

5.10 Аргументы командной строки

В операционной среде, обеспечивающей поддержку Си, имеется возможность передать аргументы или параметры запускаемой программе с помощью командной строки. В момент вызова main получает два аргумента. В первом, обычно называемом argc (сокращение от argument count), стоит количество аргументов, задаваемых в командной строке. Второй, argv (от argument vector), является указателем на массив символьных строк, содержащих сами аргументы. Для работы с этими строками обычно используются указатели нескольких уровней.

Простейший пример - программа echo ("эхо"), которая печатает аргументы своей командной строки в одной строчке, отделяя их друг от друга пробелами. Так, команда

echo Здравствуй, мир!

Напечатает

Здравствуй, мир!

По соглашению argv[0] есть имя вызываемой программы, так что значение argc никогда не бывает меньше 1. Если argc равен 1, то в командной строке после имени программы никаких аргументов нет. В нашем примере argc равен 3, и соответственно argv[0], argv[1] и argv[2] суть строки "echo", "Здравствуй," и "мир!". Первый необязательный аргумент - это argv[1], последний - argv[argc-1]. Кроме того, стандарт требует, чтобы argv[argc] всегда был пустым указателем.

Первая версия программы echo трактует argv как массив символьных указателей.

#include ‹stdio.h›

/* эхо аргументов командной строки: версия 1 */

main(int argc, char *argv[])

{

 int i;

 for (i = 1; i ‹ argc; i++)

  printf("%s%s", argv[i], (i ‹ argc-1) ? " " : ");

 printf("n");

 return 0;

}

Так как argv - это указатель на массив указателей, мы можем работать с ним как с указателем, а не как с индексируемым массивом. Следующая программа основана на приращении argv, он приращивается так, что его значение в каждый отдельный момент указывает на очередной указатель на char; перебор указателей заканчивается, когда исчерпан argc.

#include ‹stdio.h›

/* эхо аргументов командной строки; версия 2 */

main(int argc, char *argv[])

{

 while (--argc › 0)

  printf("%s%s", *++argv, (argc › 1) ? " " : ");

 print f("n");

 return 0;

}

Аргумент argv - указатель на начало массива строк аргументов. Использование в ++argv префиксного оператора ++ приведет к тому, что первым будет напечатан argv[1],а не argv[0]. Каждое очередное приращение указателя дает нам следующий аргумент, на который указывает *argv. В это же время значение argc уменьшается на 1, и, когда оно станет нулем, все аргументы будут напечатаны. Инструкцию printf можно было бы написать и так:

printf((argc › 1)? "%s ": "%s", *++argv);

Как видим, формат в printf тоже может быть выражением.

В качестве второго примера возьмем программу поиска образца, рассмотренную в параграфе 4.1, и несколько усовершенствуем ее. Если вы помните, образец для поиска мы "вмонтировали" глубоко в программу, а это, очевидно, не лучшее решение. Построим нашу программу по аналогии с grep из UNIXa, т. е. так, чтобы образец для поиска задавался первым аргументом в командной строке.

#include ‹stdio.h›

#include ‹string.h›

#define MAXLINE 1000

int getline(char *line, int max);

/* find: печать строк с образцом, заданным 1-м аргументом */

main(int argc, char *argv[])

{

 char line[MAXLINE];

 int found = 0;

 if (argc != 2)

  printf("Используйте в find образецn");

 else

  while (getline(line, MAXLINE) › 0)

   if (strstr(line, argv[1]) ›= NULL) {

    printf ("%s", line);

    found++;

   }

 return found;

}

Стандартная функция strstr(s,t) возвращает указатель на первую встретившуюся строку t в строке s или NULL, если таковой в s не встретилось. Функция объявлена в заголовочном файле ‹string.h›.

Эту модель можно развивать и дальше, чтобы проиллюстрировать другие конструкции с указателями. Предположим, что мы вводим еще два необязательных аргумента. Один из них предписывает печатать все строки, кроме тех, в которых встречается образец; второй - перед каждой выводимой строкой печатать ее порядковый номер.

По общему соглашению для Си-программ в системе UNIX знак минус перед аргументом вводит необязательный признак или параметр. Так, если -x служит признаком слова "кроме", которое изменяет задание на противоположное, а -n указывает на потребность в нумерации строк, то команда

find -x -n образец

напечатает все строки, в которых не найден указанный образец, и, кроме того, перед каждой строкой укажет ее номер.

1 ... 28 29 30 31 32 33 34 35 36 ... 81
Перейти на страницу:
Открыть боковую панель
Комментарии
Настя
Настя 08.12.2024 - 03:18
Прочла с удовольствием. Необычный сюжет с замечательной концовкой
Марина
Марина 08.12.2024 - 02:13
Не могу понять, где продолжение... Очень интересная история, хочется прочесть далее
Мприна
Мприна 08.12.2024 - 01:05
Эх, а где же продолжение?
Анна
Анна 07.12.2024 - 00:27
Какая прелестная история! Кратко, ярко, захватывающе.
Любава
Любава 25.11.2024 - 01:44
Редко встретишь большое количество эротических сцен в одной истории. Здесь достаточно 🔥 Прочла с огромным удовольствием 😈