Может ли статическая функция c вернуть локальный массив символов?

В библиотеке zflog я видел этот код

static char* lvl_char(const int lvl)
{
    switch (lvl)
    {
    case ZF_LOG_VERBOSE:
        return "VERBOSE\0";
    case ZF_LOG_DEBUG:
        return "DEBUG\0";
    case ZF_LOG_INFO:
        return "INFO\0";
    case ZF_LOG_WARN:
        return "WARN\0";
    case ZF_LOG_ERROR:
        return "ERROR\0";
    case ZF_LOG_FATAL:
        return "FATAL\0";
    default:
        ASSERT_UNREACHABLE("Bad log level");
        return "?\0";
    }
}

что показалось мне странным Можем ли мы действительно вернуть локальную строку c из статических функций?

4 ответа

Решение

Вы не правы, возвращаемый указатель не указывает на локальный char array, но к string literal, то есть static для процесса.

Из с-стандарта

6.4.5 Строковые литералы

Sematics

На этапе 7 преобразования байт или код нулевого значения добавляется к каждой многобайтовой символьной последовательности, которая получается из строкового литерала или литералов.78) Затем многобайтовая символьная последовательность используется для инициализации массива статической длительности и длины статического хранения, достаточных только для содержать последовательность.[...]

Эмефазис мой

Связь функции (здесь статическая) не имеет значения вообще. Также не возвращается "строка"; вместо этого возвращается указатель на символ. Вполне законно возвращать указатели на первый символ строковых литералов - строковые литералы гарантированно существуют на протяжении всей программы. C11 6.4.5p6 утверждает, что строковые литералы в том виде, в котором они здесь используются, используются для инициализации *"[анонимного] массива статической длительности и длины, достаточных для хранения последовательности". Статическая продолжительность хранения означает, что его "время жизни - это полное выполнение программы, а его сохраненное значение инициализируется только один раз, до запуска программы". ( C11 6.2.4p3).


То, что выглядит странно, это \0 в конце строковых литералов, так как литеральные строки всегда заканчиваются 0, так что по существу "VERBOSE\0" будет просто завершено с 2 нулевыми байтами вместо обычного; strlen для этой строки будет возвращаться 7 так же, как это вернулось бы для "VERBOSE", и так далее.

Может ли статическая функция c вернуть локальный массив символов?

TL; DR независимо от static или же extern Функции не должны возвращать локальный массив, потому что он не может использоваться осмысленно.


Теперь, чтобы проработать вопрос в руке,

.... статические функции?

У вас там было static связан со связью для "функции", а не с возвращаемым значением или типом.

Вот, static спецификатор хранения означает, что функция имеет внутреннюю связь, т. е. доступна только из модуля перевода.

связанные с C11 Глава §6.2.2

Если объявление идентификатора области файла для объекта или функции содержит спецификатор класса хранения static, идентификатор имеет внутреннюю связь.

ОТОХ, return заявления как

return "FATAL\0"; 
return "DEBUG\0"; ///and so on

фактически возвращает указатель на первый элемент строкового литерала, который по определению имеет статическую длительность хранения Примечание 1, поэтому возвращаемое значение

  • соответствует типу возврата, char * Заметка 2
  • действителен после возврата заявления. Заметка 3

Примечание 1:

квотирование C11 глава §6.4.5/ P6

На этапе 7 перевода байт или код нулевого значения добавляются к каждой многобайтовой последовательности символов, которая является результатом строкового литерала или литералов. 78) Последовательность многобайтовых символов затем используется для инициализации массива статической длительности и длины, достаточных для хранения последовательности. Для символьных строковых литералов элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой последовательности символов.

Заметка 2:

Цитирование главы §6.3.2.1/P3,

За исключением случаев, когда это операнд sizeof оператор, _Alignof оператор, или унарный & оператор или строковый литерал, используемый для инициализации массива, выражение с типом '' массив типа '' преобразуется в выражение с типом '' указатель на тип '', которое указывает на начальный элемент объекта массива и это не lvalue.

Заметка 3:

Цитирование главы §6.2.4/P3

Объект, идентификатор которого объявлен без спецификатора класса хранения _Thread_local, а также с внешней или внутренней связью или со спецификатором класса хранения static , имеет статическую продолжительность хранения. Его время жизни - это полное выполнение программы, и его сохраненное значение инициализируется только один раз, до запуска программы.

  • Может ли статическая функция c вернуть локальный массив символов? Нет, это ведет к неопределенному поведению.
  • Ваш код возвращает локальный массив символов? Нет. Возвращает указатель на строковый литерал, который находится в постоянной памяти. Эта память не локальна.
  • Можете ли вы вернуть указатель на строковый литерал из любой функции? Да.
  • Есть ли разница между обычными функциями внутренней связи static те здесь? Нет.
Другие вопросы по тегам