Использование GCC / Clang `asm` Construct
В GCC и Clang, а также в других компиляторах, поддерживающих расширенный синтаксис сборки GCC, вы можете сделать следующее:
int *unused_ptr_arg;
__asm__("" : "=x" (unused_ptr_arg));
return (*func)(unused_ptr_arg);
То __asm__
construct говорит «Вот код ассемблера, который нужно вставить в программу на данном этапе.Он записывает результат в unused_ptr_arg
в любом месте, которое вы для него выберете ». (Ограничение x
означает, что компилятор может выбрать память, регистр процессора или что-нибудь еще, что поддерживает машина.) Но фактический код сборки пуст (""
).Таким образом, ассемблерный код не генерируется, но компилятор считает, что unused_ptr_arg
был инициализирован.В Clang 6.0.0 и GCC 7.3 (последние версии в настоящее время в Compiler Explorer) для x86-64 это создает jmp
без xor
.
Использование стандартного C
Учтите это:
int *unused_ptr_arg;
(void) &unused_ptr_arg;
return (*func)(unused_ptr_arg);
Цель (void) &unused_ptr_arg;
- взять адрес unused_ptr_arg
, даже если адрес не используется.Это отключает правило в C 2011 [N1570] 6.3.2.1 2, которое гласит, что поведение не определено, если программа использует значение неинициализированного объекта продолжительности автоматического хранения, которое могло быть объявлено с помощью register
.Поскольку его адрес взят, он не мог быть объявлен с register
, и поэтому использование значения больше не является неопределенным поведением в соответствии с этим правилом.
Вследствие этого объект имеет неопределенное значение.Тогда возникает вопрос, могут ли указатели иметь представление ловушки.Если указатели не имеют представлений ловушек в используемой реализации C, тогда ловушка не будет возникать из-за простой ссылки на значение, как при передаче его в качестве аргумента.
Результат с Clang 6.0.0 в Compiler Explorer - это инструкция jmp
без настройки регистра параметров, даже если -Wall -Werror
добавлено к опциям компилятора.Напротив, если строка (void)
удалена, возникает ошибка компилятора.