Получение оборванного указателя, возвращая указатель из локального массива 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++.