#c #setjmp
Вопрос:
Я читаю код библиотеки C и не могу понять, что происходит:
struct Foo *foo = NULL;
lib_var((void *)amp;foo);
if (setjmp(get_jmp_buf()) == 0) {
foo = ...;
// other calculation that may cause longjmp
} else {
//something bad happens
drop_if_not_null(foo);
}
где lib_var
находится функция , имеющая такой прототип void lib_var(void *);
,
и такая реализация в отдельном файле C:
void lib_var(void *variable)
{
// nothing
}
Так что же это за lib_var
функция, что-то вроде volatile
того, чтобы заставить компилятор перечитывать переменную из памяти на setjmp() != 0
всякий случай?
Является ли это неопределенным поведением? Из-за того, что я уверен, что LTO удалит такой совершенно бесполезный код,
и LTO не должен изменять поведение подтверждения на стандартный код.
Комментарии:
1. Где ты это нашел?
setjmp
Функция ожидает параметр типаjmp_buf
.2. Это действительно похоже на (возможно, ошибочную) попытку гарантировать, что
foo
это будет выделено в памяти, а не в регистре, и, таким образом, его значение будет сохраненоlongjmp
и впоследствии перезагружено. Я согласен, чтоvolatile
это был бы правильный способ сделать это.3.
setjmp
вызов @dbush-это псевдокод, я удалил использованиеcontext
переменной, где была извлечена помощь служебного методаjmp_buf
4.
I read code of C library
Почему бы не сказать нам, какая это библиотека C? Является ли это открытым исходным кодом? Можете ли вы разместить ссылку на фрагмент? Не могли бы вы опубликовать полный реальный код? Вы знаете автора этого кода — почему бы не спросить его? Является ли код в версии системы управления версиями? Говорит ли о чем-то история фиксации? Поддерживался ли код несколькими людьми? Мы знаем только то, что вы нам говорите, поэтому, если вы дадите только ту информацию, которая необходима только для получения выводов, которые вы получили, мы все получим те же выводы… т. е. мы почти ничего нового не узнаем.
Ответ №1:
Да, это неопределенное поведение с точки зрения стандарта C — или, по крайней мере, значение foo
неопределенно и не может быть безопасно использовано для чего-либо.
C17 (n2176) 7.13.2.1 (3):
Все доступные объекты имеют значения, а все другие компоненты абстрактной машины имеют состояние на момент вызова функции longjmp, за исключением того, что значения объектов с длительностью автоматического хранения, которые являются локальными для функции, содержащей вызов соответствующего макроса setjmp, которые не имеют определенного типа и были изменены между вызовом setjmp и вызовом longjmp, являются неопределенными.
Именно так обстоит дело здесь: foo
имеет автоматическую продолжительность хранения и является локальной для функции, в которой setjmp
вызывается, она не volatile
квалифицирована и изменяется между setjmp
и longjmp
.
Авторы кода, вероятно, думали , что передача amp;foo
в lib_var
гарантирует, что он будет выделен в памяти, а не в регистре, и что, поскольку компилятор не мог знать, будет ли lib_var
изменяться foo
, он не мог постоянно распространять значение NULL
в else
блок. Но, как вы говорите, LTO или различные другие оптимизации могут нарушить это. Возможно, авторы намеревались поддерживать только конкретный компилятор, который, как они знали, ничего подобного не делал, или который предоставлял более сильные гарантии, чем указано в стандарте, но также возможно, что они просто запутались.
Правильным решением было бы объявить foo
как volatile
, т. е. struct Foo * volatile foo = NULL;
.