Стандартные функции работы со строками
Урок знакомит с библиотекой string.h: функции длины, копирования, склейки и сравнения строк, их главная опасность — переполнение буфера — и безопасные альтернативы.
Классические функции вроде strcpy и strcat не проверяют размер целевого буфера. Они пишут, пока не встретят нуль в источнике — и легко выходят за границы. Это причина множества громких уязвимостей в истории C.
Заголовок <string.h> даёт базовый набор функций. Самые употребимые:
#include <string.h>
char src[] = "Hello";
char dst[20];
size_t len = strlen(src); // длина: 5
strcpy(dst, src); // копируем src в dst
strcat(dst, " C!"); // дописываем в конец dst
int cmp = strcmp(dst, src); // сравнение: 0 если равны
printf("%s (длина %zu)\n", dst, strlen(dst)); // Hello C! (8)
strlen считает символы, strcpy копирует строку, strcat приклеивает одну к другой, strcmp сравнивает (возвращает 0 при равенстве, иначе разницу). Удобно — но опасно.
Как работает под капотом
Проблема strcpy в том, что она не знает, сколько места в приёмнике. Она копирует символы из источника, пока не встретит '\0' — даже если приёмник давно кончился:
char dst[4]; // место только под 4 байта
strcpy(dst, "Hello world"); // источник — 12 байт!
dst: [ H ][ e ][ l ][ l ] | o ][ ' ][ w ]... -> ПЕРЕПОЛНЕНИЕ
индексы 0..3 ^ дальше идёт ЧУЖАЯ память
|
strcpy не остановится здесь —
она пишет до '\0' источника,
затирая соседние данные на стеке.
Так возникает buffer overflow — классическая уязвимость,
которой пользуются для взлома программ.
Безопасные варианты принимают максимальный размер приёмника: strncpy(dst, src, n), strncat(dst, src, n), snprintf(dst, n, ...). Они не запишут больше n символов. Но и у них есть тонкости: strncpy, например, может не поставить завершающий нуль, если источник длиннее лимита.
Частые ошибки
- Переполнение через strcpy/strcat. Копирование в слишком маленький буфер — главный источник уязвимостей в C.
- Забыли место под нуль при копировании. Буфер должен вмещать символы плюс терминатор.
- strncpy без ручного нуля. Если источник ровно длины лимита, нуль не поставится — нужно дописать вручную.
- strcmp трактуют как «да/нет».
strcmpвозвращает 0 при равенстве, то естьif (strcmp(a,b))истинно, когда строки РАЗНЫЕ.
Best practices
- Избегайте
strcpyиstrcatс непроверенными данными. Предпочитайтеsnprintf— он удобен и всегда ставит нуль в пределах лимита. - Всегда передавайте размер приёмника в «n-версии» функций и держите буфер достаточного размера (символы + 1).
- После
strncpyвручную ставьтеdst[n-1] = '\0', чтобы гарантировать терминатор. - Проверяйте код санитайзерами:
-fsanitize=addressи valgrind находят переполнения буфера на тестах.
Логику строковых операций удобно повторить на Python через посимвольную работу со списками — так видно, где именно происходит переполнение.
# Эмулируем strcpy с проверкой размера буфера (как безопасная версия)
def safe_strcpy(dst_size, src):
result = []
for ch in src:
if len(result) >= dst_size - 1: # оставляем место под '\0'
print("СТОП: буфер переполнился бы!")
break
result.append(ch)
result.append('\0') # обязательный терминатор
return result
buf = safe_strcpy(4, "Hello") # буфер маленький — обрежется
print("Результат:", buf)
buf2 = safe_strcpy(20, "Hello") # места хватает
print("Результат:", buf2)
Та же логика на Python ▶ — мы заранее проверяем размер буфера, как делают strncpy/snprintf. Обычная strcpy такой проверки не делает и спокойно пишет за границу.
Функции памяти и поиск в строках
Помимо строковых, в <string.h> есть «байтовые» функции, не завязанные на нулевой терминатор: memcpy копирует заданное число байтов, memset заполняет область одним значением (часто — нулями), memmove безопасно копирует даже при перекрытии областей. Они работают с любыми данными — структурами, массивами чисел, — а не только со строками, и потому быстрее и универсальнее. Для разбора текста пригодятся strchr (найти символ), strstr (найти подстроку) и strtok (разбить строку на части по разделителям). У strtok есть коварная особенность: он изменяет исходную строку, вставляя в неё нули, и хранит внутреннее состояние, поэтому его нельзя использовать в многопоточном коде. Зная набор инструментов string.h, вы реже изобретаете велосипед и реже допускаете ошибки.
Итоги
Библиотека string.h даёт strlen, strcpy, strcat, strcmp и другие функции. Их опасность — отсутствие проверки размера приёмника, что ведёт к переполнению буфера, классической уязвимости C. Решение — «n-версии» (strncpy, strncat, snprintf) с явным лимитом, ручная установка нуль-терминатора и проверка кода санитайзерами. Помните: strcmp возвращает 0 при равенстве.