在C中设置一个标志,就像汇编语言一样优雅

时间:2014-04-18 23:28:50

标签: c clear c-preprocessor flags

与装配相比,C中的标志处理非常麻烦。

我正在寻找一种方法来使C代码像汇编一样可读。

在汇编中:

#define powerOn flagsByte,0
... 
bsf powerOn ; Turn on the power
bcf powerOn ; Turn off the power
btfsc powerOn ; If the power is on...

在C:

flagsByte |= (1 << 0) ; // Turn on the power
flagsByte &= ~(1 << 0) ; // Turn off the power
if (flagsByte & (1 << 0)); // If the power is on...

在C中,使用宏:

#define BIT_SET(var,bitNo) (var |= (1<<(bitNo)))
BIT_SET(flagsByte,0) ; // Turn on the power

虽然有效,但它仍然没有装配时那么干净。

我喜欢这样做:

#define powerOn flagsByte,0
BIT_SET(powerOn) ; // Turn on the power

但这不起作用,因为它扩展到:

flagsByte,0 |= (1<<())

而不是:

flagsByte |= (1<<(0))

问题:

在C中是否有一种优雅的方式来设置,清除或测试定义如下的标志?

#define powerOn flagsByte,0

6 个答案:

答案 0 :(得分:4)

就个人而言,我更喜欢位字段语法,并且没有宏,因为我的标志几乎总是在结构内部。但是,如果你坚持用C语言编写汇编程序,请按照以下方式进行:

/* We need to indirect the macro call so that the pair of arguments get expanded */
#define BITSET_(f,i) do{f|= 1<<(i);}while(0)
#define BITCLR_(f,i) do{f&=~(1<<(i));}while(0)
#define BITCHK_(f,i) ((f)&(1<<(i)))

#define BITSET(fi) BITSET_(fi)
#define BITCLR(fi) BITCLR_(fi)
#define BITCHK(fi) BITCHK_(fi)

/* Define the flag container and bit number as per OP */
#define poweron flags1,0
#define warnuser flags7,4

/* Sample uses */
BITSET(poweron);
BITCLR(warnuser);
/* Since BITCHK expands to a parenthesized expression, I can get away with
 * leaving out the parentheses in the if statement. Not saying that's a good
 * idea or anything.
 */
if BITCHK(poweron) BITSET(warnuser);

如果您有gcc,可以使用gcc -E flag_macros.c

进行验证

答案 1 :(得分:3)

这是一组与您的汇编示例紧密匹配的宏:

#define powerOn        0
#define someotherfield 1

#define BITMASK(field) (1u << (field))
#define SET(field)  do { flagsByte |= BITMASK(field); } while(0)
#define CLR(field)  do { flagsByte &= ~BITMASK(field); } while(0)
#define TEST(field) (flagsByte & BITMASK(field))

/* Use examples */
SET(powerOn);
CLEAR(powerOn);
if (TEST(powerOn)) {
    // Danger!
}

这是一个变体,允许您将变量包含在特定字段定义中。它涉及argument prescan

,有点棘手
#define powerOn        flagsByte,0                                                                                                                                                      
#define someotherfield flagsByte,1                                                                                                                                                      

#define BITMASK(field) (1u << (field))                                                                                                                                                  
#define _SET(var, field)  do { var |= BITMASK(field); } while(0)                                                                                                                        
#define SET(x) _SET(x)

/* Use examples */                                                                                                                                                                      
SET(powerOn);

答案 2 :(得分:2)

你可以#define powerOn flagsByte |= (1 << 0);,然后就像声明一样使用它。如在

// do stuff...
powerOn; // Turn power on.
// do stuff...

答案 3 :(得分:2)

使用GCC,您可以定义所谓的位字段并像结构成员一样操作它们:

struct flagsByte
  {
    unsigned int powerOn: 1;  /* single bit */
  };

flagsByte.powerOn = 0;
flagsByte.powerOn = 1;

在此基础上,可以定义一些琐碎的宏,让人联想到汇编:

#define bsf(X) flagsByte.(X) = 1
#define bcf(X) flagsByte.(X) = 0

然后简单地写

bsf(powerOn);  /* set flag   */
bcf(powerOn);  /* clear flag */

不幸的是,这并不适用于每个C编译器。

答案 4 :(得分:2)

你通过第二次扩展来做到这一点。

~/sandbox/20$ cat >t.c
#define BITSET_INNER(a,b) a |= (1<<b)
#define BITSET(spec) BITSET_INNER(spec)
#define f1 flagword,3

    BITSET(f1)

~/sandbox/20$ cc -E t.c
# 1 "t.c"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "t.c"




    flagword |= (1<<3)

添加标记粘贴和强大的峡谷,您可以从C预处理器中获得一些非常令人满意的结果。

答案 5 :(得分:1)

你可以定义一些常量,不是使用预处理器,而是使用枚举来减少意外:

enum flags{
    powerOn = 1<<0,
    powerMask = ~powerOn,
    /*...*/
};

并像这样使用它们:

flagsByte |= power;
flagsByte &= ~power;
flagsByte &= powerMask; /* same as previous line */

C(和C ++)中的一个经验法则:如果可以避免,请不要使用预处理器。

无论如何,如果您可以使用位域固有的实现定义,请使用Roberto Reale提出的位域。