setjmp/longjmp и локальные переменные
Мои вопросы направлены на поведение setjmp/longjmp относительно локальных переменных.
Пример кода:
jmp_buf env;
void abc()
{
int error;
...
if(error)
longjmp(env);
}
void xyz() {
int v1; // non-volatile; changed between setjmp and longjmp
int v2; // non-volatile; not changed between setjmp and longjmp
volatile int v3; // volatile; changed between setjmp and longjmp
volatile int v4; // volatile; not changed between setjmp and longjmp
...
if(setjmp(env)) {
// error handling
...
return;
}
v1++; // change v1
v3++; // change v3
abc();
}
int main(...) {
xyz();
}
Документация setjmp/longjmp гласит:
"Все доступные объекты имеют значения на момент вызова longjmp(), за исключением того, что значения объектов с автоматической продолжительностью хранения, локальные для функции, содержащей вызов соответствующего setjmp(), которые не имеют тип volatile-qualified и которые изменяются между вызовом setjmp() и вызовом longjmp(), не определены."
Я вижу следующие две возможные интерпретации:
intepretation1:
Локальные переменные восстанавливаются, кроме тех, которые оба
- энергонезависимый и
- изменено
intepretation2:
Локальные переменные восстанавливаются, кроме
- те, которые являются энергонезависимыми и
- те, которые изменены
Согласно интерпретации 1 после longjmp, только v1 не определено. v2, v3, v4 определены. Согласно интерпретации2 после longjmp определяется только v4. v1, v2, v3 не определены.
Какой из них прав?
Кстати: мне нужен общий ("переносимый") ответ, который действителен для всех компиляторов, т.е. попытка с одним конкретным компилятором не поможет.
2 ответа
Интерпретация 1 верна. Если бы толкование 2 было задумано, в исходном тексте использовалось бы "или которые были изменены" вместо "и".
setjmp/longjmp реализуется путем сохранения регистров (включая указатели стека и кода и т. д.) при первой передаче и восстановления их при переходе.
Автоматические (то есть "локальные", размещенные в стеке) переменные, которые не являются "volatile", могут храниться в регистрах, а не в стеке.
В этих условиях longjmp восстановит переменные этих регистров до их значения в тот момент, когда setjmp() был впервые вызван.
Кроме того, особенно умный компилятор может избегать переменных, которые могут быть выведены из состояния другой переменной, и вычислять их по требованию.
Однако, если переменная автоматическая, но ей не присвоен регистр, она может быть изменена кодом между setjmp и longjmp.
Volatile явно указывает компилятору не хранить переменную в регистре.
Поэтому, если вы явно не скажете, что переменная является изменчивой, если вы изменили переменную между setjmp/longjmp, ее значение будет зависеть от выбора, который делает компилятор, и, следовательно, вам не следует полагаться ("неопределенный").