
Продолжаем пробовать язык C. Нужно придумать себе какую-нибудь простую задачу. Самую простую. Сделаем так: объявим переменную типа «строка» с некоторым заданным значением и выведем её на экран. Это же реально просто!
В языке C объявить строку можно по-разному. Считается, что строка — это массив (последовательность) символов. Массив произвольной длины, но в «хвосте» массива размещен служебный символ '\0' (кстати, его числовое значение — 0; напомню также, что служебные символы не отображаются), который и обозначает конец строки. Не будь этого символа, длину строки определить было бы невозможно. Вернее, это была бы такая же последовательность символов, но уже не строка.
За символы в языке C «отвечает» тип char (от англ. Character — «символ»). Таким образом, строкой у нас будет массив элементов типа char. Объявить строку можно так, как объявляют массивы:
char my_sting[] = "Какой-то текст...";
…или через объявление указателя:
char *my_string = "Какой-то текст...";
Что даст идентичный результат.
Здесь мы не только объявляем строку, но и инициализируем её вполне конкретным значением. Разумеется, можно было бы явно указать длину строки (и таким образом указать, сколько элементов в строке — сколько «места» она займет в оперативной памяти):
char my_sting[17] = "Какой-то текст...";
…но нам «лениво», поэтому пусть компилятор сам это посчитает из того значения, что мы прописали.
Кстати, использование указателей — одна из важнейших особенностей языка C. Обсуждать эту тему сейчас неохота, но если говорить совсем примитивно, то указатель — это переменная, содержащая адрес другой переменной (или произвольного участка памяти).
Итак, создадим нашу вторую (здесь первая) программу, теперь в файле с именем proj02.c:
#include <stdio.h>
int main() {
// Иногда самое сложное - придумать простую задачу
char some_string[] = "Иногда самое сложное - придумать простую задачу";
printf("%s\n", some_string);
// Объявить строку можно и по-другому
char *any_string = "Странно, но это работает аналогичным образом";
printf("%s\n", any_string);
return 0;
}
Что мы сделали? Объявили и инициализировали значениями две переменные строкового типа — some_string и any_string (это имена переменных). Будем называть их просто «строки» (хотя на самом деле — это как раз указатели), понимая, что это символьные последовательности с '\0' на конце. Далее — мы вывели эти строки «на экран» (в терминал, консоль) с помощью функции printf(). Функция эта обеспечивает форматированный вывод, что и используется: в качестве аргументов мы передаем не только имя строковой переменной («что выводить»), но и служебную конструкцию "%s" («как выводить» — как строку). Кроме того, кажется, в данном случае используется так называемая интерполяция — строковое значение помещается в то самое место, где стоит управляющая конструкция "%s". Это пример. Если хотите, изучите printf() в подробностях сами.
Таким образом, первая строка объявляется как «массив элементов типа char», вторая строка — указатель на элемент типа char. И то и другое «работает». Думаю, что служебный символ '\0' в конце последовательности компилятор ставит сам (а как иначе — мы же этого не делаем), потому что видит инициализацию (присваивание значения) переменных строковыми данными. Еще один момент: мы объявляем строковые переменные непосредственно перед их использованием (а не в каком-то отдельном месте, скажем, в начале текста программы). Согласно последним стандартам языка C — «так можно» 🙂
Теперь, как и ранее, компилируем (пользуйтесь «терминалом»):
username ~/Папка_с_программой $ gcc -Wall -o proj02 proj02.c
…и запускаем:
username ~/Папка_с_программой $ ./proj02 Иногда самое сложное - придумать простую задачу Странно, но это работает аналогичным образом username ~/Папка_с_программой $
Очевидно, всё работает. Что нам еще пожелать? Конечно — посчитать количество символов в строке и вывести его на экран.
Скорее всего, есть для этого специальная функция, но, во-первых, надо вспоминать её название, во-вторых, вероятно, еще и подключать какой-нибудь модуль для работы со строками. А нам снова «лениво»… Справимся сами.
Логика наша такова: «пройдемся» в цикле по элементам строки (символам), пока не «наткнемся» на '\0'. При этом будем наращивать некий счетчик символов. При выходе из цикла значение, накопленное счетчиком, как раз и покажет нам «длину строки». Отредактируем текст нашей программы (убрав, кстати, вторую строку за ненадобностью):
#include <stdio.h>
int main() {
// Иногда самое сложное - придумать простую задачу
char some_string[] = "Иногда самое сложное - придумать простую задачу";
printf("%s\n", some_string);
// Не помню название функции, которая вычисляет длину строки
// Поэтому делаем "вручную"
int char_count = 0; // Объявляем "счетчик" и инициализируем его нулем
// Далее - цикл "с предусловием": "пока" ("условие") {"код"}
while (some_string[char_count] != '\0') {
printf("%c", some_string[char_count]); // Выводим очередной символ на экран
char_count++; // "Наращиваем" счетчик
}
// Выводим значение счетчика
printf("\nДлина строки - %d символов\n", char_count);
return 0;
}
Итак, что мы делаем: объявляем переменную-счетчик char_count типа int («знаковое целое»), присваиваем ей значение 0. Далее — цикл while с предусловием (пока «условие истинно» выполнять некий код). В выражении условия цикла обращаемся к символам строки как к элементам массива, который «индексируется» значением char_count (индексация в языке C, как известно, начинается с нуля). Если очередной символ не равен '\0' (операция сравнения !=), выполняем итерацию цикла. И в каждой итерации наращиваем значение счетчика (унарная постфиксная операция «++»). А еще — для самопроверки выводим каждый символ (учитывая формат — "%c") на экран, причем без «перевода строки» (без "\n"), чтобы символы «легли» в одну строку. Ну и после выполнения цикла — выводим на экран значение счетчика, учитывая формат (обратите внимание на символ форматирования "%d").
Компилируем и запускаем:
username ~/Папка_с_программой $ ./proj02 Иногда самое сложное - придумать простую задачу Иногда самое сложное - придумать простую задачу Длина строки - 87 символов username ~/Папка_с_программой $
Что видим в результате: строка напечатана два раза: один раз до выполнения цикла (целиком, строкой), второй — в результате выполнения цикла (посимвольно). И результат подсчета длины строки — 87 символов.
То есть как 87?!. Ну-ка, посчитаю сам. Должно быть 47 — шесть пробелов, один дефис и ровно сорок буковок. Так почему же 87? Приехали…
Видимо, придется таки вспомнить, как называется функция подсчета длины строки — strlen(). А для её использования нужно подключить string.h — заголовочный файл библиотеки (модуля) работы со строками. Итак, вносим необходимые правки:
#include <stdio.h>
#include <string.h> // Модуль для работы со строками
int main() {
// Иногда самое сложное - придумать простую задачу
char some_string[] = "Иногда самое сложное - придумать простую задачу";
printf("%s\n", some_string);
// Не помню название функции, которая вычисляет длину строки
// Поэтому делаем "вручную"
int char_count = 0; // Объявляем "счетчик" и инициализируем его нулем
// Далее - цикл "с предусловием": "пока" ("условие") {"код"}
while (some_string[char_count] != '\0') {
printf("%c", some_string[char_count]); // Выводим очередной символ на экран
char_count++; // "Наращиваем" счетчик
}
// Выводим значение счетчика
printf("\nДлина строки, что сами посчитали - %d символов\n", char_count);
// А тут длину строки узнаем с помощью функции strlen()
printf("Длина строки, что функция strlen() вернула - %d символов\n", (int)strlen(some_string));
return 0;
}
Мы подключили библиотеку для работы со строками — #include <string.h>. А ближе к концу текста программы мы передаем на печать результат вызова функции strlen() с именем строки в качестве аргумента — strlen(some_string). Поскольку strlen() возвращает значение типа size_t, а функции printf() нужно передать значение типа int, выполняем «приведение типа» — (int)strlen(some_string).
И снова — компилируем и выполняем:
username ~/Папка_с_программой $ ./proj02 Иногда самое сложное - придумать простую задачу Иногда самое сложное - придумать простую задачу Длина строки, что сами посчитали - 87 символов Длина строки, что функция strlen() вернула - 87 символов username ~/Папка_с_программой $
И снова 87! Ну что тут скажешь — по крайней мере, мы в цикле подсчитали длину строки «не хуже», чем функция strlen(). Хотя всё равно неправильно. Кажется, я догадываюсь в чем дело (подсказка — unicode). И даже предполагаю, как можно решить эту проблему (одним из возможных способов). Но это — в следующий раз.
P.S. Заметка была почти готова, когда я наткнулся в Википедии на на такой момент: приведение результата функции strlen() к типу int для функции printf() можно не делать, если символ форматирования %d заменить на %zu. Таким образом, можно заменить строку
printf("Длина строки, что функция strlen() вернула - %d символов\n", (int)strlen(some_string));
на более простой вариант
printf("Длина строки, что функция strlen() вернула - %zu символов\n", strlen(some_string));
и всё будет прекрасно работать. Думаю, для тех, кто в отличие от меня язык C знает, это не новость 🙂