Получение оборванного указателя, возвращая указатель из локального массива C-стиля
Я немного смущен следующим кодом:
#include <iostream>
const char* f()
{
const char* arr[]={"test"};
return arr[0];
}
int main()
{
auto x = f();
std::cout << x;
}
На мой взгляд, этот код должен быть UB (неопределенное поведение). Мы возвращаем указатель на элемент массива C-стиля внутри локальной области. Все должно пойти не так. Тем не менее, ни один из компиляторов, с -Wall -Wextra -pedantic
я тестировал, не жаловался (я использовал -Wall -Wextra -pedantic
как на g++, так и на clang). valgrind
тоже не жалуется.
Является ли код выше действительным или это UB, как можно было бы подумать?
PS: работает, кажется, производит "правильный" результат, то есть отображает "тест", но это не указывает на правильность.
Ответы
Ответ 1
Нет, это не UB.
Это:
const char* f()
{
const char* arr[]={"test"};
return arr[0];
}
Можно переписать эквивалент:
const char* f()
{
const char* arr0 = "test";
return arr0;
}
Поэтому мы просто возвращаем локальный указатель в строковый литерал. Строковые литералы имеют статическую продолжительность хранения, ничего не болтается. Функция действительно такая же, как:
const char* f()
{
return "test";
}
Если вы сделали что-то вроде этого:
const char* f() {
const char arr[] = "test"; // local array of char, not array of char const*
return arr;
}
Теперь это UB - мы возвращаем висячий указатель.
Ответ 2
Массив arr
имеет локальную продолжительность хранения и исчезнет в конце области действия. Строковый литерал "test"
однако, является указателем на статическое хранилище. Временно сохраняя этот указатель в локальном массиве arr
перед его возвратом, это не изменится. Он всегда будет местом статического хранения.
Обратите внимание, что если функция должна была возвращать строковый тип стиля C++ вместо C-стиля const char *
, дополнительное преобразование/ведение бухгалтерии, вероятно, оставит вас с ограничением по времени в соответствии с временными правилами C++.