Рис. 11.2. Прямоугольные и зубчатые массивы
Символьные строки и строковые функции 429
Указатели и строки
Возможно, вы уже заметили, что в этом обсуждении строк мы периодически ссылаемся на указатели. Большинство операций со строками в С фактически работают с указателями. В качестве примера рассмотрим короткую программу в листинге 11.5.
Листинг 11.5. Программа p_and_s. с

На заметку!
Если ваш компилятор не поддерживает спецификатор %р, замените его %u или %iu.
Глядя на эту программу, можно подумать, что она создает копию строки "Не позволяйте себя запутать ! ", и на первый взгляд вывод подтверждает это предположение:
Не позволяйте себя запутать ! !
mesg = Не позволяйте себя запутать!; smesg = 0x0012ff48; value = 0х0040а000
copy = Не позволяйте себя запутать!; &сору = 0x0012ff44; value = 0х0040а000
Но давайте взглянем на вывод printf() более внимательно. Прежде всего, значения mesg и сору выводятся в виде строки (%s). Здесь нет никакого сюрприза; все строки выглядят как "Не позволяйте себя запутать ! ".
Следующий элемент в каждой строке представляет адрес определенного указателя. Для конкретного запуска два указателя mesg и сору хранятся, соответственно, в ячейках 0x0012ff48 и 0x0012ff44.
Теперь обратите внимание на завершающий элемент по имени value. Это значение заданного указателя. Значением указателя является адрес, который он содержит. Как видите, mesg указывает на ячейку 0х0040а000, и то же самое можно сказать о сору. Следовательно, сама строка не копировалась. Оператор сору = mesg; всего лишь создает второй указатель, ссылающийся на ту же строку.
Тогда зачем все эти предосторожности? Почему бы просто не скопировать всю строку? Задайте себе вопрос: что эффективнее — копировать один адрес или, скажем, 50 отдельных элементов? Часто для решения задачи вполне достаточно только адреса. Если вам действительно необходима копия строки, т.е. ее дубликат, можете воспользоваться функцией strcpy() или strncpy(), как будет показано далее в главе.
Теперь, когда мы обсудили определение строк внутри программы, давайте переключимся на строки, вводимые с клавиатуры.
430 глава 11
Ввод строк
Если в программе нужно ввести строку, понадобится сначала зарезервировать пространство для хранения этой строки и затем с помощью функции ввода извлечь строку.
Создание пространства под строку
Первое действие связано с подготовкой места, куда будет помещена строка после ее чтения. Как упоминалось ранее, это означает выделение пространства, достаточного для последующего чтения строк. Не следует ожидать, что компьютер подсчитает длину строки во время ее чтения и выделит необходимое пространство. Компьютер нс будет делать это (если только вы не напишете специальную функцию). Например, предположим, что имеется такой код:
char *name;
scanf("%s", name);
Возможно, компилятор примет этот код, скорее всего, выдав предупреждение, но при чтении пате строка может быть записана поверх данных или кода вашей программы и вызвать ее аварийное завершение. Причина в том, что функция scanf() копирует информацию по адресу, предоставленному аргументом, а в этом случае аргументом является неинициализированный указатель; name может указывать куда угодно. Большинство программистов находят такую ситуацию крайне забавной, но только не в собственных программах.
Проще всего решить эту проблему, включив в объявление явный размер массива:
char name[81];
Теперь name представляет собой адрес зарезервированного блока памяти размером 81 байт. Другая возможность предполагает применение функций выделения памяти из библиотеки С, и мы рассмотрим их в главе 12.
После установки пространства для строки ее можно прочитать. Библиотека С предлагает три функции, позволяющие считывать строки: scanf(), gets() и fgets(). Чаще других используется функция gets(), поэтому мы обсудим ее первой.
Неудачливая функция gets()
Вспомните, что при чтении строки функция scanf() и спецификатор %s обеспечивают считывание только одного слова. Однако часто желательно, чтобы программа могла читать сразу всю вводимую строку, а не одиночное слово. Именно такой цели на протяжении многих лет служила функция gets(). Это простая и легкая в использовании функция. Она читает всю строку вплоть до символа новой строки, отбрасывает этот символ и сохраняет остальные символы, добавляя нулевой символ, чтобы образовать строку С. Часто эта функция применяется в сочетании с функцией puts(), которая отображает с троку с добавлением в конце символа новой строки. В листинге 11.6 приведен короткий пример.
Читать дальше