代码之家  ›  专栏  ›  技术社区  ›  user318904

我能用C/C++预处理器添加数字吗?

  •  13
  • user318904  · 技术社区  · 14 年前

    当然,在实际生产代码中这样做也不是一个好主意。我只是好奇地问。

    8 回复  |  直到 7 年前
        1
  •  12
  •   Agnius Vasiliauskas    13 年前

    你可以相对容易地写一个宏,它把两个整数相加 二元的 . 例如-将两个4位二进制整数求和的宏:

    #include "stdio.h"
    
    // XOR truth table
    #define XOR_0_0 0
    #define XOR_0_1 1
    #define XOR_1_0 1
    #define XOR_1_1 0
    
    // OR truth table
    #define OR_0_0 0
    #define OR_0_1 1
    #define OR_1_0 1
    #define OR_1_1 1
    
    // AND truth table
    #define AND_0_0 0
    #define AND_0_1 0
    #define AND_1_0 0
    #define AND_1_1 1
    
    // concatenation macros
    #define XOR_X(x,y) XOR_##x##_##y
    #define   OR_X(x,y) OR_##x##_##y
    #define  AND_X(x,y) AND_##x##_##y
    #define OVERFLOW_X(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) OVERFLOW_##rc1 (rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
    
    // stringification macros
    #define STR_X(x) #x
    #define STR(x) STR_X(x)
    
    // boolean operators
    #define XOR(x,y) XOR_X(x,y)
    #define   OR(x,y) OR_X(x,y)
    #define  AND(x,y) AND_X(x,y)
    
    // carry_bit + bit1 + bit2
    #define BIT_SUM(carry,bit1,bit2) XOR(carry, XOR(bit1,bit2))
    // carry_bit + carry_bit_of(bit1 + bit2)
    #define CARRY_SUM(carry,bit1,bit2) OR(carry, AND(bit1,bit2))
    
    // do we have overflow or maybe result perfectly fits into 4 bits ?
    #define OVERFLOW_0(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) SHOW_RESULT(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
    #define OVERFLOW_1(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) SHOW_OVERFLOW(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
    
    // draft-horse macros which performs addition of two 4-bit integers
    #define ADD_BIN_NUM(a1,a2,a3,a4, b1,b2,b3,b4) ADD_BIN_NUM_4(0,0,0,0, 0,0,0,0, a1,a2,a3,a4, b1,b2,b3,b4)
    #define ADD_BIN_NUM_4(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) ADD_BIN_NUM_3(rc1,rc2,rc3,AND(CARRY_SUM(0,a4,b4),OR(a4,b4)), rb1,rb2,rb3,BIT_SUM(0,a4,b4), a1,a2,a3,a4, b1,b2,b3,b4)
    #define ADD_BIN_NUM_3(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) ADD_BIN_NUM_2(rc1,rc2,AND(CARRY_SUM(rc4,a3,b3),OR(a3,b3)),rc4, rb1,rb2,BIT_SUM(rc4,a3,b3),rb4, a1,a2,a3,a4, b1,b2,b3,b4)
    #define ADD_BIN_NUM_2(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) ADD_BIN_NUM_1(rc1,AND(CARRY_SUM(rc3,a2,b2),OR(a2,b2)),rc3,rc4, rb1,BIT_SUM(rc3,a2,b2),rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
    #define ADD_BIN_NUM_1(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)      OVERFLOW(AND(CARRY_SUM(rc2,a1,b1),OR(a1,b1)),rc2,rc3,rc4, BIT_SUM(rc2,a1,b1),rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
    #define OVERFLOW(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) OVERFLOW_X(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4)
    #define   SHOW_RESULT(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) STR(a1) STR(a2) STR(a3) STR(a4) " + " STR(b1) STR(b2) STR(b3) STR(b4) " = " STR(rb1) STR(rb2) STR(rb3) STR(rb4)
    #define   SHOW_OVERFLOW(rc1,rc2,rc3,rc4, rb1,rb2,rb3,rb4, a1,a2,a3,a4, b1,b2,b3,b4) STR(a1) STR(a2) STR(a3) STR(a4) " + " STR(b1) STR(b2) STR(b3) STR(b4) " = overflow"
    
    void main()
    {
        printf("%s\n", 
            ADD_BIN_NUM(
                        0,0,0,1, // first  4-bit int
                        1,0,1,1) // second 4-bit int
                        );
    
        printf("%s\n", 
            ADD_BIN_NUM(
                        0,1,0,0, // first  4-bit int
                        0,1,0,1) // second 4-bit int
                    );
    
        printf("%s\n", 
            ADD_BIN_NUM(
                        1,0,1,1, // first  4-bit int
                        0,1,1,0) // second 4-bit int
                    );
    }
    

    这个宏可以很容易地扩展为两个8位或16位甚至32位整数的加法。 所以基本上,我们所需要的就是标记连接和替换规则,以实现宏的惊人结果。

    编辑:

    嗯!

        2
  •  12
  •   James McNellis    14 年前

    #if #elif 指令。除此之外,在预处理过程中,数字并不是真正的数字;它们被分类为 代币,实际上不是数字。

    您可以使用令牌连接来评估基本算法:

    #define ADD_0_0 0
    #define ADD_0_1 1
    #define ADD_1_0 1
    #define ADD_1_1 2
    
    #define ADD(x, y) ADD##_##x##_##y
    
    ADD(1, 0) // expands to 1
    ADD(1, 1) // expands to 2
    

    但实际上,没有理由这么做,这样做是很愚蠢的(你必须定义大量的宏才能让它发挥更大的作用)。

    更明智的做法是将宏扩展为可由编译器计算的整型常量表达式:

    #define ADD(x, y) ((x) + (y))
    
    ADD(1, 1) // expands to ((1) + (1))
    

    编译器将能够计算 1 + 1 表情。

        3
  •  5
  •   Chris Schmich    14 年前

    #include <iostream>
    using namespace std;
    
    template <int N, int M>
    struct Add
    {
        static const int Value = N + M;
    };
    
    int main()
    {
        cout << Add<4, 5>::Value << endl;
        return 0;
    }
    
        4
  •  5
  •   Isaac To    12 年前

    在预处理器中进行有界整数加法是完全可能的。而且,它实际上比人们真正希望的更经常地被需要,也就是说,仅仅拥有它的替代品 ((2) + (3)) 在程序中不起作用。(例如,不能有一个名为 x((2)+(3))

    #define INC(x) INC_ ## x
    #define INC_0 1
    #define INC_1 2
    #define INC_2 3
    #define INC_3 4
    #define INC_4 5
    #define INC_5 6
    #define INC_6 7
    #define INC_7 8
    #define INC_8 9
    #define INC_9 10
    INC(7) // => 8
    

    现在我们知道如何把加法加到1。

    #define ADD(x, y) ADD_ ## x(y)
    #define ADD_0(x) x
    #define ADD_1(x) INC(x)
    ADD(0, 2) // => 2
    ADD(1, 2) // => 3
    

    为了增加更大的数字,你需要某种“递归”。

    #define ADD_2(x) ADD_1(INC(x))
    #define ADD_3(x) ADD_2(INC(x))
    #define ADD_4(x) ADD_3(INC(x))
    #define ADD_5(x) ADD_4(INC(x))
    #define ADD_6(x) ADD_5(INC(x))
    #define ADD_7(x) ADD_6(INC(x))
    #define ADD_8(x) ADD_7(INC(x))
    #define ADD_9(x) ADD_8(INC(x))
    #define ADD_10(x) ADD_9(INC(x))
    ADD(5, 2) // => 7
    

    然而,在这方面必须小心。例如,以下操作不起作用。

    #define ADD_2(x) INC(ADD_1(x))
    ADD(2, 2) // => INC_ADD_1(2)
    

    对于此类技巧的任何扩展使用,Boost预处理器是您的朋友。

        5
  •  4
  •   Ferruccio    13 年前

    Boost Preprocessor 库,你可以用预处理器做各种事情,甚至整数 addition .

        6
  •  1
  •   Chris    14 年前

    C预处理器可以计算包含整数运算的条件。它不会替换算术表达式并将结果传递给编译器,但编译器将对编译时常量的算术进行求值,并将结果发送到二进制文件中,只要您没有重载正在使用的运算符。

        7
  •  1
  •   supercat    14 年前

    预处理器宏实际上不能做算术运算,但是可以利用它们来做枚举运算。一般的技巧是让一个宏调用其他宏,并且可以使用其他宏的不同定义重复调用。

    例如,类似于:

    #define MY_THINGS \
      a_thing(FRED,4) \
      a_thing(GEORGE,6) \
      a_thing(HARRY,5) \
      a_thing(HERMIONE,8) \
      a_thing(RON,3) \
      // This line left blank 
    
    #define a_thing(name,size) EN_##name}; enum {EN_SIZE_##name=(size),EN_BLAH_##name = EN_##name+(size-1),
    enum {EN_FIRST_THING=0, MY_THINGS EN_TOTAL_SIZE};
    #undef a_thing
    

    这将允许一个人为数组中的每件事物“分配”一定数量的空间。数学不是由预处理器完成的,但是枚举仍然被视为编译时常量。

        8
  •  0
  •   greatwolf Romowski    14 年前

    我很肯定C/C++预处理器只是复制和粘贴,实际上它并不评估任何表达式。表达式求值由编译器完成。