Может ли препроцессор C выполнять целочисленную арифметику?

Как говорится в вопросе, способен ли препроцессор C сделать это?

Например:

#define PI 3.1416
#define OP PI/100
#define OP2 PI%100

Есть ли способ рассчитать OP и / или OP2 на этапе предварительной обработки?

13.10.2009 13:44:29
PI не является целым числом в примере; следовательно, ни OP, ни OP2 не были бы целыми, если бы препроцессор был вынужден их оценивать. И операнды '%' не могут быть числами с плавающей запятой.
Jonathan Leffler 13.10.2009 13:58:29
6 ОТВЕТОВ
РЕШЕНИЕ

Целочисленная арифметика? Запустите следующую программу, чтобы узнать:

#include "stdio.h"
int main() {
    #if 1 + 1 == 2
        printf("1+1==2\n");
    #endif
    #if 1 + 1 == 3
        printf("1+1==3\n");
    #endif
 }

Ответ «да», есть способ заставить препроцессор выполнять целочисленную арифметику, то есть использовать его в условии препроцессора.

Обратите внимание, что ваши примеры не являются целочисленной арифметикой. Я только что проверил, и препроцессор gcc не работает, если вы попытаетесь сделать это сравнение с плавающей запятой. Я не проверял, допускает ли когда-либо стандарт арифметику с плавающей точкой в ​​препроцессоре.

Обычное раскрытие макроса не оценивает целочисленные выражения, оно оставляет его компилятору, что можно увидеть по предварительной обработке (-E в gcc):

#define ONEPLUSONE (1 + 1)
#if ONEPLUSONE == 2
    int i = ONEPLUSONE;
#endif

Результат int i = (1 + 1);(плюс, вероятно, некоторые вещи, чтобы указать имена исходных файлов и номера строк и тому подобное).

35
11.05.2012 04:53:13
Крис: Сейчас мы живем в будущем, и пока летающие машины все еще отсутствуют, возвращаются значения по умолчанию из основной функции!
Thomas Padron-McCarthy 13.10.2009 14:19:39
@Chris: тесная фигурная скобка, достижение которой «возвращает значение 0» (5.1.2.2.3, завершение программы). Если мне не позволено использовать стандарт C в вопросе, помеченном буквой «C», то к чему идет мир? Разве вы не можете найти кого-то, кто использует комментарии в стиле //, которые могут быть видны компилятору C89, и вместо этого жаловаться на них? ;-)
Steve Jessop 13.10.2009 14:50:59
@Chris - Закрывающая скобка? :-)
Chris J 13.10.2009 14:51:32
О нет, я только что понял, что имел в виду Крис! Из-за способа, которым SO форматирует код, в моей программе нет завершающей строки ! Это даже не недопустимая программа, которую должен диагностировать компилятор, это неопределенное поведение. Ужас, тревога и общая паника! Я должен это исправить сразу. ;-p
Steve Jessop 13.10.2009 15:12:21

Код, который вы написали, на самом деле не заставляет препроцессор выполнять какие-либо вычисления. #Define выполняет простую замену текста, поэтому с этим определено:

#define PI 3.1416
#define OP PI/100

Этот код:

if (OP == x) { ... }

становится

if (3.1416/100 == x) { ... }

и тогда это компилируется. Компилятор, в свою очередь, может выбрать такое выражение, вычислить его во время компиляции и создать код, эквивалентный этому:

if (0.031416 == x) { ... }

Но это компилятор, а не препроцессор.

Чтобы ответить на ваш вопрос, да, препроцессор МОЖЕТ сделать некоторую арифметику. Это видно, когда вы пишете что-то вроде этого:

#if (3.141/100 == 20)
   printf("yo");
#elif (3+3 == 6)
   printf("hey");
#endif
19
13.10.2009 14:02:32
К сожалению, если вы пытаетесь ваш условной компиляции фрагмент кода, вы получите сообщение от GCC: x.c:5:6: error: floating constant in preprocessor expression. Вы можете делать целочисленную арифметику с препроцессором C; Вы не можете делать арифметику с плавающей точкой с ним.
Jonathan Leffler 13.10.2009 14:00:47
выглядит как срезанные углы gcc, Stroustrup C ++ 2nd edition r.16.5 #if constant-expression, r.5.19 constant expression«... плавающие константы должны быть преобразованы в целочисленные типы».
denis 23.02.2013 17:23:22

Да , я имею в виду: он может делать арифметику :)

Как продемонстрировано в 99 бутылках пива .

7
13.10.2009 13:59:38

Да.

Я не могу поверить, что никто еще не связался с каким-то запутанным победителем конкурса Си. Парень реализовал ALU в препроцессоре с помощью рекурсивных включений. Вот реализация, а здесь что-то из объяснения.

Тем не менее, вы не хотите делать то, что сделал этот парень. Это забавно и все, но посмотрите на время компиляции в его файле подсказок (не говоря уже о том, что полученный код не поддерживается). Чаще всего люди используют препроцессор строго для замены текста, и вычисление постоянной целочисленной арифметики происходит либо во время компиляции, либо во время выполнения.

Однако, как отмечали другие, вы можете сделать некоторую арифметику в операторах #if.

4
13.10.2009 14:03:40
stackoverflow.com/questions/652788/… - chaos-pp. Злоупотребление препроцессором со стилем.
Andrew Y 15.10.2009 16:50:57

Да, это можно сделать с помощью Boost Preprocessor. И он совместим с чистым Си, так что вы можете использовать его в программах на Си только с компиляциями на Си. Ваш код включает в себя числа с плавающей точкой, так что я думаю, что это должно быть сделано косвенно.

#include <boost/preprocessor/arithmetic/div.hpp>
BOOST_PP_DIV(11, 5) // expands to 2
#define KB 1024
#define HKB BOOST_PP_DIV(A,2)
#define REM(A,B) BOOST_PP_SUB(A, BOOST_PP_MUL(B, BOOST_PP_DIV(A,B)))
#define RKB REM(KB,2)

int div = HKB;
int rem = RKB;

Это препроцессирует (проверьте с помощью gcc -S)

int div = 512;
int rem = 0;

Благодаря этой теме.

7
23.05.2017 10:30:01

Будьте осторожны при выполнении арифметики: добавьте скобки .

#define SIZE4 4
#define SIZE8 8
#define TOTALSIZE SIZE4 + SIZE8

Если вы когда-нибудь используете что-то вроде:

unsigned int i = TOTALSIZE/4;

и ожидайте, что я буду 3, вы получите 4 + 2 = 6 вместо этого. Добавить скобку:

#define TOTALSIZE (SIZE4 + SIZE8)
2
10.07.2019 16:55:09