Может ли строковый литерал быть возвращен без malloc/буфера

#c

Вопрос:

Является ли следующий допустимый/приемлемый код C для возврата строкового литерала?

 char* level_str(level)
{
    switch (level) {
        case DEBUG: return "DEBUG";
        case INFO:  return "INFO";
        case WARN:  return "WARN";
        case ERROR: return "ERROR";
        default:    return "UNKNOWN";
    }
}
 

Или указатель на строковый литерал становится недопустимым/переопределяется в стеке после возврата функции? Если да, то есть только два допустимых способа сделать это: (1) удалить строку; или (2) записать в буфер, предоставленный вызываемым абонентом?

Комментарии:

1. Строковые литералы хранятся в ready-only разделе, а не в стеке. Таким образом, возврат указателя на него из функции безопасен.

2. @chux-RestorateMonica не могли бы вы, пожалуйста, уточнить, что вы под этим подразумеваете? Означает ли это, что указатель/ char* входит в rax , и вызываемый абонент может использовать это, и поэтому строковые литералы в порядке?

3. Ключ-это адрес строкового литерала (например "hello world" ), который фиксирован и хорош для жизни программы. Таким образом, этот адрес не уничтожается при возврате функции (это просто литерал, доступный только для чтения, который является частью исполняемого файла). Таким образом, в отличие от локально объявленного массива, созданного в стеке функций, адрес строковых литералов может быть объявлен и возвращен из функции. Во время компиляции компилятор видит "hello world" и просто делает его частью .rodata раздела исполняемого файла. С a char array[12]; компилятор понятия не имеет, что это будет.

4. @DavidC.Rankin «литерал только для чтения, который является частью исполняемого файла / / /» -это деталь реализации, хотя и распространенная. Ни код, ни среда не обязаны иметь раздел «только для чтения».

5.Ок, просто о любой другой компилятор на Земле, за исключением нестандартного соответствующие компиляторы, написанные в Microsoft, строковые литералы, перейдите в .rodata раздел исполняемого файла… но, знаю, что это реализация деталь, и есть очень популярный компилятор, который позволяет изменять строковые литералы вопреки тому, что большая часть остальной мир :) С11 стандарт — 6.4.5(Р7) "If the program attempts to modify such an array, the behavior is undefined."

Ответ №1:

Может ли строковый литерал быть возвращен без malloc/буфера

ДА. Адрес строкового литерала можно перенастроить.

Код OP хорош, но лучше, const char* level_str(level) поскольку данные const char * указывают на то, что строковые литералы лучше оставить в покое, не пытаясь изменить.

Попытка изменить строковый литерал-это неопределенное поведение (UB). Может сработать, может не сработать, может произойти сбой и т. Д.

Ответ №2:

Да, строковый литерал ведет себя как указатель на массив char , имеющий статическую длительность хранения и инициализированный заданными символами плюс завершающий нуль. Это означает, что это похоже на то, как если бы вы написали static char my_debug_string[] = { 'D', 'E', 'B', 'U', 'G', 0 }; и вернули указатель на my_debug_string (за исключением того, что вы не должны изменять массив). В частности, срок службы этого массива составляет весь срок службы программы; он остается действительным после возврата из функции, в которой он появился.

Ссылка: Стандарт C17 6.4.5 (6-7).