Найдите и объясните неопределённое поведение в этом фрагменте на C и предложите безопасные альтернативы (с сохранением функциональности): char* bad() { char buf[10]
Коротко — в этом фрагменте есть несколько проблем. Самая важная: возврат указателя на локальный массив приводит к неопределённому поведению (буфер живёт в стеке и перестаёт существовать при выходе из функции). Также в приведённом фрагменте синтаксическая ошибка (нет `;`) и отсутствует `return`. Почему это UB - Локальный массив: указатель, возвращённый из функции, будет указывать на область стека, которая может быть перезаписана — использование такого указателя после возврата даёт неопределённое поведение. - Если буфер не инициализирован или при записи выход за границы (overflow), — тоже UB. - Если буфер используется как C‑строка, отсутствие завершающего `'\0'` даёт UB при вызове строковых функций. Безопасные альтернативы (функциональность сохранить — вернуть строку/буфер): 1) Выделение в куче (caller освобождает) #include
#include
#include
char* good_malloc(void) { char *buf = malloc(101010); if (!buf) return NULL; snprintf(buf, 101010, "%s", "hello"); // гарантированная NUL-терминация return buf; } // вызов: char *s = good_malloc(); ... free(s); 2) Caller предоставляет буфер (без утечек, потокобезопасно) #include
#include
void good_out(char *out, size_t out_size) { if (out_size == 0) return; snprintf(out, out_size, "%s", "hello"); } // вызов: char buf[101010]; good_out(buf, sizeof buf); 3) Статический буфер (простo, но не потокобезопасно/не реентерабельно) #include
char* good_static(void) { static char buf[101010]; strcpy(buf, "hello"); return buf; } // последующие вызовы перезапишут буфер 4) Использовать strdup (если строка известна или формируется во временном буфере) #include
char* good_strdup(void) { return strdup("hello"); // caller free() } Краткие рекомендации - Предпочитайте вариант где caller освобождает память (malloc/strdup) или где caller предоставляет буфер. - Никогда не возвращайте указатель на локальную переменную. - Всегда гарантируйте NUL‑терминацию и избегайте переполнений (используйте snprintf/strlcpy/explicit bounds).
Почему это UB
- Локальный массив: указатель, возвращённый из функции, будет указывать на область стека, которая может быть перезаписана — использование такого указателя после возврата даёт неопределённое поведение.
- Если буфер не инициализирован или при записи выход за границы (overflow), — тоже UB.
- Если буфер используется как C‑строка, отсутствие завершающего `'\0'` даёт UB при вызове строковых функций.
Безопасные альтернативы (функциональность сохранить — вернуть строку/буфер):
1) Выделение в куче (caller освобождает)
#include #include #include
char* good_malloc(void) {
char *buf = malloc(101010);
if (!buf) return NULL;
snprintf(buf, 101010, "%s", "hello"); // гарантированная NUL-терминация
return buf;
}
// вызов: char *s = good_malloc(); ... free(s);
2) Caller предоставляет буфер (без утечек, потокобезопасно)
#include #include
void good_out(char *out, size_t out_size) {
if (out_size == 0) return;
snprintf(out, out_size, "%s", "hello");
}
// вызов: char buf[101010]; good_out(buf, sizeof buf);
3) Статический буфер (простo, но не потокобезопасно/не реентерабельно)
#include
char* good_static(void) {
static char buf[101010];
strcpy(buf, "hello");
return buf;
}
// последующие вызовы перезапишут буфер
4) Использовать strdup (если строка известна или формируется во временном буфере)
#include
char* good_strdup(void) {
return strdup("hello"); // caller free()
}
Краткие рекомендации
- Предпочитайте вариант где caller освобождает память (malloc/strdup) или где caller предоставляет буфер.
- Никогда не возвращайте указатель на локальную переменную.
- Всегда гарантируйте NUL‑терминацию и избегайте переполнений (используйте snprintf/strlcpy/explicit bounds).