Рассмотрите фрагмент на C: char *f() { char buf[16]; strcpy(buf, "this is a very long string"); return buf; } — объясните, почему код опасен, какие конкретные проблемы это вызывает (UB, переполнение стека, безопасность) и предложите несколько корректных альтернатив с объяснением компромиссов

5 Ноя в 15:03
2 +2
0
Ответы
1
Коротко — почему код опасен
- Две независимые ошибки:
1) buf — автоматическая (stack) переменная; функция возвращает указатель на память, которая перестаёт быть валидной при выходе -> висячий указатель (dangling pointer) и неопределённое поведение (UB).
2) strcpy копирует строку длиннее буфера: литерал "this is a very long string" имеет длину 262626 символов (без нуль-терминатора), с терминирующим '\0' — 272727, а буфер — 16\:1616. Получается переполнение стека на 27−16=1127-16 = 112716=11 байт → ещё одно UB и возможная порча стека/адреса возврата.
Какие конкретные проблемы это вызывает
- Немедленное UB: программа может упасть, вернуть мусор, зависнуть или «работать» непредсказуемо.
- Переполнение стека: повреждение соседних переменных, фреймов, возвращаемого адреса → коррумпция данных, аварийное завершение.
- Безопасность: классическая поверхность для эксплойтов (stack-smashing → выполнение произвольного кода или обход защиты).
- Отладка затруднена: поведение часто зависит от компилятора/оптимизаций/платформы.
Корректные альтернативы (с компромиссами)
1) Вернуть указатель на строковый литерал
```c
const char *f(void) {
return "this is a very long string";
}
```
Плюсы: просто, безопасно (литерал в статической области). Минусы: нельзя модифицировать строку; возврат типа должен быть `const char *`.
2) Статический буфер внутри функции
```c
char *f(void) {
static char buf[32];
strcpy(buf, "this is a very long string");
return buf;
}
```
Плюсы: безопасная по времени жизни (не висячий указатель). Минусы: не потокобезопасно, один и тот же буфер для всех вызовов; фиксированный размер.
3) Выделение памяти в куче (malloc), освобождение — на вызывающем
```c
char *f(void) {
const char *s = "this is a very long string";
size_t n = strlen(s) + 1;
char *buf = malloc(n);
if (!buf) return NULL;
memcpy(buf, s, n);
return buf;
}
/* Вызывающий обязан free(buf) */
```
Плюсы: динамический размер, потокобезопасно. Минусы: ответственность за free, возможны утечки, накладные расходы аллокации.
4) Буфер, выделяемый вызывающим (рекомендуемый шаблон в C)
```c
int f(char *out, size_t out_size) {
const char *s = "this is a very long string";
if (snprintf(out, out_size, "%s", s) >= (int)out_size) return -1; // недостаточно места
return 0;
}
/* Вызывающий предоставляет память и отвечает за её размер */
```
Плюсы: явная ответственность за владение, нет аллокаций внутри функции. Минусы: требует, чтобы вызывающий знал необходимый размер; нужно проверять возврат.
5) Безопасные копии/утилиты
- Используйте `snprintf` или `strlcpy` (если доступна) вместо `strcpy`. `strncpy` не является идеальным (не гарантирует нуль-терминатор).
- Всегда проверяйте длину (`strlen`) перед копированием либо выделяйте `strlen + 1`.
6) В C++ — возвращайте std::string
```cpp
std::string f() { return "this is a very long string"; }
```
Плюсы: безопасное владение, удобство. Минусы: переход в C++ язык/рантайм.
Краткий вывод / рекомендация
- Нельзя возвращать указатель на стек. Для простого неизменяемого текста — возвращайте строковый литерал (`const char *`). Для переменной длины/безопасности — либо `malloc` (и документировать освобождение), либо интерфейс с буфером, предоставленным вызывающим и защищённый `snprintf`. Для проектов на C++ — предпочтительней `std::string`.
5 Ноя в 15:32
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир