Выделение указателя на статические данные вне области видимости в C
Отказ от ответственности: этот вопрос строго академичен. Пример, который я собираюсь дать, - это, вероятно, плохой стиль.
Предположим, что в C я напишу подпрограмму этого вида:
char *foo(int x)
{
static char bar[9];
if(x == 0)
strcpy(bar, "zero");
else
strcpy(bar, "not zero"),
return bar;
}
Затем, в другом месте, я использую foo
следующим образом:
printf("%i is %s\n", 5, foo(5));
Моя ментальная модель указателей и статических переменных предсказывает, что на практике вывод этого printf будет
5 не равно нулю
... но действительно ли это требуется стандартом C, или я нахожусь на территории носового демона?
Чтобы сделать вещи еще хуже, что-то вроде
strcpy(foo(5), "five");
Моя ментальная модель говорит, что это должно "работать", если оно явно не является незаконным, хотя оно несколько бессмысленно, поскольку оно не влияет на вывод foo
. Но опять же, действительно ли это определено стандартом?
Ответы
Ответ 1
То, что вы написали, ОК; вас ждут носовые демоны. Даже пример strcpy()
- "ОК", потому что вы не вытаскиваете за пределы массива. Это "ОК" в кавычках, потому что это не очень хорошая идея, но, как написано, нет доступа к границам памяти и, следовательно, никакого поведения undefined. Данные static
в функции присутствуют на протяжении всего жизненного цикла программы, содержащие последнее значение, которое было написано на нем.
При попытке могут возникнуть проблемы:
printf("%i is %s but %i is %s\n", 5, foo(5), 0, foo(0));
Вы получите неправильный ответ для одного из двух чисел, но он не определен, что будет неправильным ответом.
Ответ 2
Ну, так как вам нужна цитата из стандарта:
Время жизни объекта - это часть выполнения программы в течение которого хранилище гарантировано зарезервировано для него.
Объект, идентификатор которого объявлен без спецификация класса хранения _Thread_local и либо с внешней, либо с внутренней связью или с типом хранилища класса static, имеет статическую продолжительность хранения. Его lifetime - это полное выполнение программы и ее сохраненное значение инициализируется только один раз, до запуска программы.
Ответ 3
При любом условии foo()
возвращает указатель на статическую переменную, срок жизни которой постоянный (т.е. он живет, когда управление проходит через него в первый раз до конца программы). Это прекрасно.
Логика вашего кода менее тонкая; например, функция не является повторной и не является потокобезопасной, и, конечно же, операция с неохраняемой строкой является прямым самоубийством.
Ответ 4
Я не вижу ничего плохого в этом коде.
Указатель, возвращаемый foo()
, является допустимым указателем, и вам разрешено разыгрывать его.
Изменить: Под "ничего плохого" я подразумеваю, что все синтаксически нормально, но я согласен с другими ответами, что этот код не подходит ни в каком значении этого слова по разным причинам. Это просто правильно в соответствии со стандартом C.
Ответ 5
В этом нет ничего плохого.
Лучший стиль будет
const char *foo(int x);
Тогда вы не можете вставлять в него, не отбрасывая const. Если вы действительно хотите сломать его, ничто не остановит вас.
если вы хотите сделать его повторным участником.
const char *foo(int x)
{
return (x? "not zero" : "zero");
}