与装配相比,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
答案 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提出的位域。