вызовите пустую функцию с адресом локальной переменной перед setjmp, для чего?

#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; .