用C语言和assemby语言一样优雅地设置标志
与汇编语言相比,C语言中的标记处理感觉很麻烦 我正在寻找一种使C代码像汇编代码一样可读的方法 在汇编中:用C语言和assemby语言一样优雅地设置标志,c,c-preprocessor,flags,C,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... #define bsf(X) flagsByte.(X) = 1 #define bcf(X) flagsByte.(X
#define powerOn flagsByte,0
...
bsf powerOn ; Turn on the power
bcf powerOn ; Turn off the power
btfsc powerOn ; If the power is on...
#define bsf(X) flagsByte.(X) = 1
#define bcf(X) flagsByte.(X) = 0
在C中:
但这不起作用,因为它扩展到:
flagsByte,0 |= (1<<())
您可以
#定义powerOn flagsByte |=(1使用GCC,您可以定义所谓的位字段,并像处理结构成员一样操作它们:
struct flagsByte
{
unsigned int powerOn: 1; /* single bit */
};
flagsByte.powerOn = 0;
flagsByte.powerOn = 1;
在此基础上,可以定义两个简单的宏,让人想起汇编:
#define powerOn flagsByte,0
...
bsf powerOn ; Turn on the power
bcf powerOn ; Turn off the power
btfsc powerOn ; If the power is on...
#define bsf(X) flagsByte.(X) = 1
#define bcf(X) flagsByte.(X) = 0
然后简单地写
bsf(powerOn); /* set flag */
bcf(powerOn); /* clear flag */
不幸的是,这并不适用于每一个C编译器。这里有一组与您的汇编示例非常匹配的宏:
#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!
}
#定义开机0
#定义其他字段1
#定义位掩码(字段)(1u您可以只定义一些常量,而不使用预处理器,而是使用枚举来减少意外:
enum flags{
powerOn = 1<<0,
powerMask = ~powerOn,
/*...*/
};
在C(和C++)中有一个很好的经验法则:如果可以避免,请不要使用预处理器
无论如何,如果您能够接受位字段固有的实现定义,请按照Roberto Reale的建议使用位字段。您可以通过第二次扩展来实现这一点
~/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)
~/sandbox/20$cat>t.c
#定义位集_INNER(a,b)a |=(1就我个人而言,我更喜欢位域语法,并且不使用宏,因为我的标志几乎总是在结构内部。但是,如果你坚持用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);
/*我们需要间接调用宏,以便展开这对参数*/
#define BITSET_f(f,i)do{f|=1有趣。谢谢。但是我有几十个标志,我真的无法想象写几十个宏,三个宏(一个用来设置,一个用来清除,一个用来测试)对于每个标志。啊,我不知道。ArtemB似乎有一个优雅的解决方案可以解决这个问题。谢谢。它并没有完全回答我的问题,假设#定义powerOn flagsByte,0。在你的解决方案中,“flagsByte.powerOn”确实包含“flagsByte”一词,它没有汇编那么优雅。不过,非常好,谢谢。位字段不是gcc扩展。它们是标准C的一部分,因此在任何符合规范的编译器上都应该可用。“#定义bsf(X)flagsByte.(X)=1”这意味着我需要一个单独的宏来处理每个变量的位。这包括各种特殊的函数寄存器,以及各种字节的标志。我不希望这样。“struct flagsByte”将适用于标记字节,我将根据需要对其进行定义。但不适用于特殊功能寄存器,该寄存器已由项目中包含的处理器文件定义。如果您希望在多个寄存器中设置多个位(如您在对ArtemB答案的评论中所述),您将不可避免地必须同时引用寄存器/变量和要设置的位。按照您的方法,我需要为每个要操作其位的变量使用3个单独的宏。这包括各种特殊函数寄存器,以及各种字节的标志。这将是几十个宏。我不会期待我很喜欢你的“位掩码(字段)”(1u我已经用允许在字段定义中使用变量名的宏的变体更新了我的答案。但是,IMHO,这几乎是预处理器滥用。我检查了您的更新,是的,它现在完全回答了我的问题;谢谢。我看到您使用的解决方案与jthill和rici提供的相同:2个宏扩展。我不确定为“优雅”设置目标变量或寄存器当有数百个寄存器被CPU和外设所暴露时,例如,考虑一个代码>重置< /代码>位。是WiFi芯片的<代码>重置< /代码>?AES加密引擎?TX数据路径?RX数据路径?DCM时钟?串行端口?看门狗?CPU等等。我相信CLR(SerrxIntFLG)。(用一个#define serxinterflg PIF1,U1RxIF,其他地方)比PIF1可读性好得多&=!(哇!谢谢!你的方法似乎和rici的方法一样,对吧?De nada,hf,感谢atcha:-)提振了我的情绪。(编辑:是的。)太好了!那就可以了。你的方法似乎和jthill的方法一样,对吧?也就是说,诀窍是做两次宏展开。@DavideAndrea:显然是这样。我没有看到他的答案。嗯,@rici,你的先到,所以我把它标记为可接受的解决方案。我发现你的解决方案比标准的C解决方案更优雅,尽管可能是Be没有大会那么优雅。我真的很感谢你。
#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!
}
#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);
enum flags{
powerOn = 1<<0,
powerMask = ~powerOn,
/*...*/
};
flagsByte |= power;
flagsByte &= ~power;
flagsByte &= powerMask; /* same as previous line */
~/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)
/* 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);