Это нормально для longjmp перед вызовом va_end?

В этом Q & A установлено, что вы всегда должны называть va_end():

Для чего именно va_end? Всегда ли нужно называть это?

Но что, если кусок кода longjmp, прежде чем вы достигнете va_end? Есть ли какое-либо обещание на стороне va_end, что все будет хорошо? Или концептуально может ли это (например) сделать выделение памяти в va_start(), которое было бы пропущено, а просто использовать трюки с помощью стека?

Ответ 1

В аргументе C99 явно указано, что va_start может выделять память, которая заканчивается освобождением va_end, именно то, что вы догадались в своем вопросе

7.15.1.2 Макрос va_copy

[...]

30 Более простой подход заключается в том, чтобы скопировать объект va_list, используемый для представления обработки аргументов. Однако нет надежного способа сделайте это на C89, потому что объект может содержать указатели на память, выделенные макросом va_start и уничтоженные макросом va_end.
Новый макрос va_copy обеспечивает этот безопасный механизм.

[...]

Итак, да, вы должны вызвать va_end до longjmp. По крайней мере, у вас в противном случае была бы утечка памяти при такой реализации.


Предположительно, Pyramid OSx имела реализацию, где распределения памяти выполнялись с помощью va_start. Аргументы функции были переданы в регистры. Это имело место даже для вариационных функций. Он может иметь предварительное изобретение ANSI C для прототипов функций, то есть вызывающий не знал бы, имеет ли он дело с вариационной функцией. va_start выделенная память, предположительно для хранения значений параметров функции таким образом, чтобы va_arg мог легко получить к ней доступ. va_end освобождает выделенную память.

Его реализация va_start и va_end фактически требовала синтаксически сопоставления va_start и va_end, потому что это был тот, который использовал несбалансированные фигурные скобки, поэтому ANSI C уже запретил эту реализацию, но тот же самый принцип можно было бы сделать работая при согласовании фигурных скобок.

Я могу найти очень мало конкретной информации об этой реализации, это просто кусочки на Usenet в конце 80-х, начале 90-х. То, что я нашел, может быть неполным или даже просто неправильным. Более подробная информация очень приветствуется, особенно тем, кто использовал эту реализацию самостоятельно.

Ответ 2

Если вы используете jmp_buff, хранящийся в глобальной переменной (обычный шаблон), вероятно, было бы безопасно сделать ее копию и использовать setjmp, чтобы longjmp переместился в ваш код а не внешнего вызывающего абонента; в случае longjmp, ваш код может затем вызвать va_end и longjmp с помощью сохраненной копии буфера; если ваш код выходит из строя, ему нужно будет восстановить глобальный буфер перед возвратом.