В общем случае компилятору сложно точно знать, к каким объектам функция может иметь доступ и, следовательно, потенциально может изменять их. В момент, когда вызывается putchar()
, GCC не знает, может ли быть реализация putchar()
, которая могла бы модифицировать running
, поэтому он должен быть несколько пессимистичным и предположить, что running
на самом деле мог быть изменен.
Например, позже в единице перевода может быть реализация putchar()
:
int putchar( int c)
{
running = c;
return c;
}
Даже если в единице перевода нет реализации putchar()
, может быть что-то, что могло бы, например, передать адрес объекта running
, чтобы putchar
мог его изменить:
void foo(void)
{
set_putchar_status_location( &running);
}
Обратите внимание, что ваша функция handler()
доступна глобально, поэтому putchar()
может вызвать саму handler()
(напрямую или иным образом), что является примером описанной выше ситуации.
С другой стороны, поскольку running
виден только единице трансляции (то есть static
), к тому моменту, как компилятор доберется до конца файла, он должен определить, что putchar()
не может получить к нему доступ. (при условии, что это так), и компилятор может вернуться и «исправить» пессимизацию в цикле while. забастовка>
Поскольку running
является статическим, компилятор может определить, что он недоступен извне единицы перевода, и выполнить оптимизацию, о которой вы говорите. Однако, поскольку он доступен через handler()
, а handler()
доступен извне, компилятор не может оптимизировать доступ. Даже если вы сделаете handler()
статическим, он будет доступен извне, так как вы передадите его адрес другой функции.
Обратите внимание, что в вашем первом примере, хотя то, что я упомянул в предыдущем абзаце, по-прежнему верно, компилятор может оптимизировать доступ к running
, потому что «абстрактная машинная модель», на которой основан язык C, не принимает во внимание асинхронную активность, за исключением в очень ограниченных обстоятельствах (одно из которых является ключевым словом volatile
, а другое — обработкой сигналов, хотя требования к обработке сигналов недостаточно сильны, чтобы компилятор не мог оптимизировать доступ к running
в вашем первом примере).
На самом деле, вот что C99 говорит о поведении абстрактной машины примерно в этих обстоятельствах:
5.1.2.3/8 "Выполнение программы"
ПРИМЕР 1:
Реализация может определить однозначное соответствие между абстрактной и фактической семантикой: в каждой точке следования значения реальных объектов согласуются со значениями, заданными абстрактной семантикой. Ключевое слово volatile
тогда будет избыточным.
В качестве альтернативы реализация может выполнять различные оптимизации в каждой единице перевода, чтобы фактическая семантика согласовывалась с абстрактной семантикой только при выполнении вызовов функций через границы единиц перевода. В такой реализации во время каждого входа в функцию и возврата функции, когда вызывающая функция и вызываемая функция находятся в разных единицах перевода, значения всех внешне связанных объектов и всех объектов, доступных через указатели в них, согласуются с абстрактной семантикой. . Более того, в момент входа в каждую такую функцию значения параметров вызываемой функции и всех объектов, доступных через указатели в ней, будут согласовываться с абстрактной семантикой. В этом типе реализации объекты, на которые ссылаются подпрограммы обслуживания прерываний, активируемые сигнальной функцией, требуют явного указания энергозависимой памяти, а также других ограничений, определенных реализацией.
Наконец, вы должны отметить, что стандарт C99 также говорит:
7.14.1.1/5 "Функция signal
Если сигнал возникает не в результате вызова функции abort
или raise
, поведение не определено, если обработчик сигнала ссылается на любой объект со статической продолжительностью хранения, кроме присвоения значения объекту, объявленному как volatile sig_atomic_t
...
Так что, строго говоря, переменную running
может потребоваться объявить как:
volatile sig_atomic_t running = 1;
person
Michael Burr
schedule
25.03.2010
handler
статическим. Он все еще не оптимизируется? - person Adam Goode   schedule 25.03.2010handler
статическим и опустите вызовsignal
, цикл будет оптимизирован. Если вы либо сделаетеhandler
нестатическим, либо поместите вызовsignal
, он не будет его оптимизировать. Итак, что он может подумать, так это то, чтоsignal
вызываетhandler
напрямую, используя переданный указатель функции (а не прерывание)? - person Johannes Schaub - litb   schedule 25.03.2010signal
хранит указатель наhandler
где-то в глобальном массиве, а затемputchar
вызывает его. Вот почему компилятору не разрешено оптимизировать в этом случае. - person Thomas   schedule 25.03.2010signal
, как и для других встроенных функций. Тогда он будет знать, что переданный указатель функции не вызывается из контекста обработки сигнала. Или, может быть, они явно хотят разрешить изменение энергонезависимых переменных в обработчиках сигналов, и это поведение предусмотрено? - person Johannes Schaub - litb   schedule 25.03.2010putchar
может позвонитьhandler
. - person David Schwartz   schedule 05.10.2013