Если я напишу #определять, который выполняет операцию с использованием других констант препроцессора, вычисляется ли окончательное значение каждый раз, когда макрос появляется во время выполнения? Зависит ли это от оптимизаций в компиляторе или это предусмотрено стандартом?
Пример:
#define EXTERNAL_CLOCK_FREQUENCY 32768
#define TIMER_1_S EXTERNAL_CLOCK_FREQUENCY
#define TIMER_100_MS TIMERB_1_S / 10
Будет ли операция 32768/10 выполняться во время выполнения каждый раз, когда я использую макрос TIMER_100_MS?
Я бы хотел избежать следующего:
#define EXTERNAL_CLOCK_FREQUENCY 32768
#define TIMER_1_S EXTERNAL_CLOCK_FREQUENCY
#define TIMER_100_MS 3276
Компилятор должен уметь вычислять константные интегральные выражения, потому что они необходимы для вычисления таких вещей, как размеры массивов во время компиляции. Однако в стандарте говорится только, что они «могут» - но не «должны» - делать это. Следовательно, только безмозглый компилятор не оценил бы постоянные интегральные выражения во время компиляции, но простая проверка вывода сборки для нетрадиционного компилятора проверила бы каждый случай.





Во время компиляции. Это стандарт языка (и всегда был), не зависящий от компилятора.
Редактировать
Комментатор попросил ссылку - цитата из "Язык программирования C" 2-го издания приложения A12.3 (стр. 229):
A control line of the form
#define identifier token-sequencecauses the preprocessor to replace subsequent instances of the identifier with the given sequence of tokens; leading and trailing whitespace around the roken sequence is discaded
Конец редактирования
Я совершенно запуталась. Вы публикуете ответ, который противоречит этому (говорит, что они «могут» быть оценены во время перевода, а не «должны» быть.
@David: макрос будет РАСШИРЕН во время компиляции (как еще это могло быть), но нет гарантия, что выражение «TIMERB_1_S / 10» (то есть деление) не оценивается во время выполнения.
Я не понимаю, насколько это актуально. Если это отвечает на заданный вопрос, вопрос необходимо переписать.
Will the operation 32768 / 10 occur at runtime every time I use the TIMERB_100_MS macro?
Каждое место в вашем коде, где вы используете TIMERB_100_MS, будет заменено препроцессором на 32768 / 10.
Будет ли это выражение дополнительно оптимизировано (оно оценивается как константа) зависит от вашего компилятора.
Я не знаю ни одного стандарта, который бы гарантировал его оптимизацию. Препроцессор заменит 32768/10 на TIMER_100_MS, что вы можете увидеть, запустив gcc -c. Чтобы узнать, оптимизируется ли компилятор дальше, запустите gcc -S и проверьте ассемблер. С gcc 4.1, даже без каких-либо флагов оптимизации, это сокращается до константы во время компиляции:
#include <stdlib.h>
#include <stdio.h>
#define EXTERNAL_CLOCK_FREQUENCY 32768
#define TIMER_1_S EXTERNAL_CLOCK_FREQUENCY
#define TIMER_100_MS TIMER_1_S / 10
int main(int argc, char **argv)
{
printf("%d\n", TIMER_100_MS);
return(0);
}
gcc -S test.c
cat test.s
...
popl %ebx
movl $3276, 4(%esp)
leal LC0-"L00000000001$pb"(%ebx), %eax
movl %eax, (%esp)
call L_printf$stub
...
Используя эту технику, вы можете показать, что даже сложные математические операции, такие как log(3.0), будут преобразованы во встроенные немедленные значения, хотя при разборке log(3.0) будет кодироваться как 4607626529066517259, что является 64-битным целочисленным преобразованием эквивалентного IEEE double ...
Макросы - это просто текстовая подстановка, поэтому в вашем примере запись TIMER_100_MS в программе - это интересный способ записи 32768 / 10.
Следовательно, вопрос в том, когда компилятор будет оценивать 32768 / 10, которое является постоянным интегральным выражением. Я не думаю, что стандарт требует здесь какого-либо особого поведения (поскольку оценка времени выполнения и времени компиляции по сути неотличима), но любой наполовину достойный компилятор оценит его во время компиляции.
Это ключевой момент. Препроцессор манипулирует текстом, затем компилятор получает его и знает ничего такого о том, сколько предварительной обработки было сделано раньше ...
Даже если препроцессор сделает это, он не сможет этого сделать, пока он не определен таким образом: #define TIMER_100_MS (TIMERB_1_S / 10) Поскольку вы не указываете выражение в паре, было бы сложно вычислить его как 3277, где вы могли бы написать 1 / TIMER_100_MS ( Я полагаю, где результат в любом случае не был бы желаемым) Но если бы препроцессор уже оценил это выражение здесь, он даже нарушил бы порядок приоритета оператора.
Из Проект комитета WG14 / N1124 - 6 мая 2005 г. ISO / IEC 9899: TC2:
6.6 Constant expressions
Syntax
constant-expression:
conditional-expressionDescription
A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.
Constraints
Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated.96)
Each constant expression shall evaluate to a constant that is in the range of epresentable values for its type.
Спасибо за ссылку на ваш источник, это ценный ресурс.
Компилятор должен оптимизировать это выражение. Я не думаю, что это требуется стандартом, но я никогда не видел компилятора, который НЕ выполнял бы эту задачу.
Однако НЕ следует писать:
#define TIMER_100_MS TIMERB_1_S / 10
... потому что это ошибка, ожидающая своего появления. Вы всегда должны заключать в скобки # определения, содержащие выражения.
#define TIMER_100_MS (TIMERB_1_S / 10)
Рассмотреть возможность :
i = 10 * TIMER_100_MS;
Первый случай даст 32768 ((10 * TIMERB_1_S) / 10), второй 32760 (10 * (TIMERB_1_S / 10)). Разница здесь не критическая, но вы ДОЛЖНЫ знать об этом!
Очень хороший момент, и это на самом деле критическая разница: вы можете захотеть сделать x% TIMER_100_MS, и без скобок это сначала возьмет мод x на TIMERB_1_S, а затем разделит результат на 10. Ранее была такая ошибка при реализации кольцевого буфера: )
Большинство ответов здесь сосредоточено на эффекте макрозамены. Но я думаю, он хотел знать,
32768 / 10
оценивается во время компиляции. Прежде всего, это арифметическое постоянное выражение и, кроме того, целочисленное постоянное выражение (потому что оно содержит только литералы целочисленного типа). Реализация может рассчитывать его во время выполнения, но она также должна иметь возможность вычислять его во время компиляции, потому что
Если компилятор в принципе может вычислить результат уже во время компиляции, он должен использовать это значение, а не пересчитывать его во время выполнения, я думаю. Но, возможно, для этого есть еще какая-то причина. Я бы не знал.
Редактировать: Мне очень жаль, что я ответил на вопрос, как если бы он касался C++. Вы заметили, что сегодня вы спросили о C. Переполнение в выражении считается неопределенным поведением в C, независимо от того, происходит это в константном выражении или нет. Второй момент, конечно же, верен и для Си.
Редактировать: Как отмечается в комментарии, если макрос подставляется в выражение, подобное 3 * TIMER_100_MS, то это оценивает (3 * 32768) / 10. Следовательно, простой и прямой ответ - «Нет, это не будет происходить каждый раз во время выполнения, потому что деление может вообще не происходить из-за правил приоритета и ассоциативности». Мой ответ выше предполагает, что макрос всегда заменяется таким образом, что деление действительно происходит.
Строго говоря, это неправильно. Из-за подстановки макросов вы не можете знать, что это оценивается, поскольку имеет значение, что появляется рядом с ним после замены. Например, 3 * 32768 / 10 может отличаться от 32768 / 10 * 3 в целочисленном выражении из-за порядка операций слева направо. Если бы определение макроса окружило выражение (), то ответ был бы верным.
@Brick, но в этом случае это не «операция 32768/10». Однако вопрос касался только «операции 32768/10». Итак, мой ответ ничего не говорит о случае «3 * 32768/10», потому что это «(3 * 32768) / 10». Мой ответ верен по поводу «32768/10 * 3» и других случаях, когда есть такая операция.
Это вопрос, скопированный напрямую: «Будет ли операция 32768/10 выполняться во время выполнения каждый раз, когда я использую макрос TIMER_100_MS?» Это определенно об использовании макроса, и макрос вычисляется без скобок. Вы не сможете узнать, упрощается это или нет, если не знаете все места, где используется макрос.
Ребята, это преобразование называется «сворачиванием констант», и его делают даже большинство школьных компиляторов. Если у вас есть компилятор, созданный кем-то, а не вами или вашим соседом по комнате в колледже, и вы компилируете статически типизированный язык, вы можете рассчитывать на него даже без включения оптимизации. Другое дело, если вы имеете дело с каким-то дурацким динамическим языком, которому разрешено изменять значение /.
Постоянное складывание! вот что я искал.
Это неправильно, компиляторы не могут управлять числами с плавающей запятой во время компиляции. Если вас устраивает значение 3276 во время компиляции, тогда все в порядке, но компилятор не сможет оценить это во время компиляции с точностью с плавающей запятой. Оптимизация с плавающей запятой слишком сложна для компиляторов, потому что оптимизация числа с плавающей запятой может привести к неожиданным результатам в математических выражениях, поэтому достойный компилятор (любая версия gcc, любая версия clang, любая версия msvc, любая версия icc) не упростит его до 3276,8, конец истории.
И другая часть вашего вопроса, вы спросили, будет ли она оцениваться для каждого расширения макроса. Опять же, если вас устраивает значение 3276, тогда ответ отрицательный, это зависит от компилятора, уровня оптимизации и фона, его можно поместить в таблицу констант или встроить в код. Вы никогда не будете вычислять одно и то же выражение во время выполнения для каждого расширения макроса. Опять же, если вам нужна точность с плавающей запятой, чтобы получить 3276,8, тогда это выражение будет оцениваться для каждого расширения макроса во время выполнения.
Ознакомьтесь с дополнительными аспектами компиляции и оптимизации: http://www.agner.org/optimize/#manuals
Результат целочисленного деления четко определен. Где в игру вступает математика с плавающей запятой?
обратите внимание, что
TIMERB_1_S / 10- это3276, а не3277