Неформатированные ввод из стандартного потока и вывод в стандартный поток
Функция printf()
позволяет легко вывести на экран строку с пробелами:
printf("%s", "Hello world");
Однако ввести строку, содержащую произвольное количество пробелов с scanf()
не удастся. Для scanf()
любой пробел является границей завершения
ввода текущих данных, за исключением случая чтения символа.
Для таких случаев полезной может оказаться функция getchar()
, которая позволяет вводить данные посимвольно:
int i;char str[20];
for (i=0; (str[i] = getchar()) != '\n'; i++);
str[i] = '\0';
printf("\n%s\n", str);
В заголовке цикла getchar()
возвращает символ, который записывается в текущую ячейку массива. Если символ равен '\n', цикл заканчивается. Затем он затирается
символом '\0'. В примере специально не добавлен проверочное условие для предотвращения выхода за пределы массива.
На языке программирования C можно использовать более простые способы работы со строками. Функции стандартной библиотеки gets()
и puts()
применяются для ввода строк из стандартного потока и их вывода. Буква s в названиях функций указывает на слово string (строка).
Обе функции принимают указатель на массив символов (это может быть имя массива или указатель).
Функция gets()
записывает введенные символы в массив, указанный в качестве аргумента. Символ перехода на новую строку игнорируется. В свою очередь, функция
puts()
выводит строку и добавляет символ новой строки самостоятельно. Самый простой пример их использования:
char str[20];
gets(str);
puts(str);
Таким образом, если вам достаточно работать со строками, а посимвольная обработка не требуется, функция puts()
и gets()
будут удобным выбором.
(Однако, стоит отметить, что функцию gets() признали небезопасной, и она была удалена из стандарта C11.)
Массив символов и указатель на строку
Строка — это массив символов, последний элемент которого — это нулевой символ '\0' в кодировке ASCII. При работе со строками, как и с числовыми массивами, можно использовать указатели. Вы можете объявить массив символов, записать в него строку, а затем установить указатель на первый или любой другой элемент массива, работая с ним через указатель:
char name[30];
char *nP;
printf("Введите имя и фамилию: ");
gets(name);
printf("Имя: ");
for(nP = name; *nP != ' '; nP++)
putchar(*nP);
printf("\nФамилия: ");
puts(nP+1);
Цикл начинается с присвоения указателю адреса первого элемента массива. Затем значение указателя увеличивается до тех пор, пока не будет достигнут пробел. Таким образом, с помощью указателя можно получить вторую часть строки.
Иногда в программах можно увидеть подобные объявления и определения для указателя-строки:char \*strP = "Hello World!";
Строку, присвоенную указателю, можно также вывести на экран, обратившись по указателю:puts(strP);
Но какова разница между строкой, присвоенной указателю, и строкой, присвоенной массиву?
Когда в программе объявляются переменные и определяется память для данных, изменять эти данные во время выполнения программы нельзя, если они не были присвоены переменным.
Что это значит в нашем примере? В программу вводится строка-константа (литерал), ссылка на первый элемент которой присваивается указателю. Вы можете менять значение указателя для перехода к любым элементам строкового массива или даже начать ссылаться на другую строку. Но изменять элементы самой строки нельзя. Это можно подтвердить таким примером кода:
char *strP;
strP = "This is a literal"; // работает, но строку нельзя изменить
puts(strP);
printf("%c\n",strP[3]);
strP[3] = 'z'; // ошибка
Попытка изменить строку-константу в последней строке кода приведет к ошибке.
В таком случае недопустимо делать следующее:
char *strP;
scanf("%s",strP); // ошибка сегментации
В этой ситуации память под массив символов не выделена, есть только указатель. Поэтому записать строку некуда. Если же память выделена путем объявления массива и адрес массива присвоен указателю, ситуация меняется:
char str[12];
char *strP;
strP = str;
gets(strP); // память выделена под массив заранее
puts(strP);
Используйте указатель, если требуются неизменяемые массивы символов.
Передача строки в функцию
Передача строки в функцию происходит аналогично передаче массивов чисел:
void change (char *s) {
for (;*s != '\0'; s++)
(*s)++;
}
Объявите три массива символов в программе. Получите данные для двух из них с помощью функции gets()
. Третий массив
должен хранить результат конкатенации (объединения) двух введенных строк. Напишите функцию для выполнения конкатенации строк.
Массив строк и массив указателей
Рассмотрим более сложный пример. У нас есть набор строк. Нужно отсортировать их по длине, начиная с самых коротких:
Набор строк можно представить как двумерный массив, где каждый одномерный массив — это строка символов: char str[][10] = {"Hello", "World", "!!!", "&&&"};
Сортировка строк требует перемещения значений в ячейках памяти, что может быть затруднительно для компьютера при большом количестве строк. Однако, есть иной подход. Можно создать массив указателей, где каждый элемент указывает на строку в первом массиве. Далее сортируем указатели, что более эффективно. Хотя сам набор строк не отсортируется, с помощью указателей комплексный "срез" будет упорядочен:
#include <stdio.h>
#include <string.h>
#define N 6
void sortlen(char *s[]);
int main() {
char strings[N][30];
char *strP[N];
int i;
for(i=0; i<N; i++) {
gets(strings[i]);
strP[i] = &strings[i][0];
}
printf("\n");
sortlen(strP);
for(i=0; i<N; i++) {
printf("%s\n",strP[i]);
}
}
void sortlen(char **s) { // **s == *s[] - псевдомассив указателей
int i, j;
char *str;
for (i=0; i<N-1; i++)
for (j=0; j < N-i-1; j++) {
if (strlen(s[j]) > strlen(s[j+1])) {
str = s[j];
s[j] = s[j+1];
s[j+1] = str;
}
}
}
Замечания:
- Параметром функции
sortlen()
является указатель на указатель или, что проще, массив указателей на символы. Мы передаем в функцию указатель на первый элемент массиваstrP
. - Сортировка выполняется методом пузырька: если длина строки, на которую указывает следующий указатель массива strP, меньше длины строки по текущему указателю, значения указателей меняются.
- Выражение
strP[i] = &strings[i][0]
означает, что элементу массива указателей присваивается адрес первого символа строки.
Реализуйте программу, которая сортирует строки по алфавиту. Для упрощения задачи упорядочивание выполняется только по первой букве строк (при одинаковых начальных буквах дальнейшая проверка не требуется).