Кейс (C): объясните, что делает и в чём ошибка в следующем фрагменте — почему поведение неопределённо и как это исправить: char *s = malloc(10); strcpy(s, "this string is way too long"); free(s); strcpy(s, "another");
Кратко: в коде две проблемы, обе вызывают неопределённое поведение (UB). 1) Переполнение буфера: выделено 101010 байт, затем копируется строка длиннее этой памяти — это записывает за границы выделенного блока и ломает кучу/данные -> UB (возможны крах, молчалое повреждение памяти, уязвимость). 2) Использование после освобождения (use-after-free): после `free(s)` указатель больше не указывает на валидную память, `strcpy(s, ...)` снова записывает в освобождённый участок -> UB. Как исправить (варианты): - Выделять ровно столько памяти, сколько нужно: char *s = malloc(strlen("this string is way too long") + 1); if (!s) { /* обработка ошибки */ } strcpy(s, "this string is way too long"); free(s); s = malloc(strlen("another") + 1); strcpy(s, "another"); free(s); Здесь размеры указаны как strlen("...")+1strlen("...") + 1strlen("...")+1. - Или использовать `realloc` вместо `free`+нового `malloc`: char *s = malloc(strlen(...)+1); /* ... */ s = realloc(s, strlen("another")+1); strcpy(s, "another"); free(s); - Или использовать удобные/безопасные функции: - `strdup("...")` (POSIX) для одновременного выделения и копирования; - `snprintf` или `strncpy` с явной границей длины (но с осторожностью: `strncpy` не гарантирует завершающий NUL). - После `free(s)` безопаснее присвоить `s = NULL;`, чтобы лишний `strcpy(s, ...)` сразу давал явные ошибки (или проверять). Итого: выделяйте достаточный объём памяти (strlen(строка)+1strlen(\text{строка})+1strlen(строка)+1), не записывайте вне границ и не используйте указатель после `free`.
1) Переполнение буфера: выделено 101010 байт, затем копируется строка длиннее этой памяти — это записывает за границы выделенного блока и ломает кучу/данные -> UB (возможны крах, молчалое повреждение памяти, уязвимость).
2) Использование после освобождения (use-after-free): после `free(s)` указатель больше не указывает на валидную память, `strcpy(s, ...)` снова записывает в освобождённый участок -> UB.
Как исправить (варианты):
- Выделять ровно столько памяти, сколько нужно:
char *s = malloc(strlen("this string is way too long") + 1);
if (!s) { /* обработка ошибки */ }
strcpy(s, "this string is way too long");
free(s);
s = malloc(strlen("another") + 1);
strcpy(s, "another");
free(s);
Здесь размеры указаны как strlen("...")+1strlen("...") + 1strlen("...")+1.
- Или использовать `realloc` вместо `free`+нового `malloc`:
char *s = malloc(strlen(...)+1);
/* ... */
s = realloc(s, strlen("another")+1);
strcpy(s, "another");
free(s);
- Или использовать удобные/безопасные функции:
- `strdup("...")` (POSIX) для одновременного выделения и копирования;
- `snprintf` или `strncpy` с явной границей длины (но с осторожностью: `strncpy` не гарантирует завершающий NUL).
- После `free(s)` безопаснее присвоить `s = NULL;`, чтобы лишний `strcpy(s, ...)` сразу давал явные ошибки (или проверять).
Итого: выделяйте достаточный объём памяти (strlen(строка)+1strlen(\text{строка})+1strlen(строка)+1), не записывайте вне границ и не используйте указатель после `free`.