#include <stdio.h>
typedef enum {
false = (int) 0, true = (int) 1
} bool;
inline void set_bit(register unsigned int *x,
register const unsigned short int offset, register const bool value) {
if (value)
*x |= (1 << offset);
else
*x &= ~(1 << offset);
}
inline bool get_bit(register const unsigned int x,
register const unsigned short int offset) {
return (x & (1 << offset)) != 0;
}
int main(void) {
unsigned int x = 0;
int count;
for (count = 0; count < 8; count += 2)
set_bit(&x, count, true);
for (count = 7; count >= 0; count--)
printf("%d ", (int) get_bit(x, count));
printf("\n");
return 0;
}
#1 楼
但是,我坚持要明确所有内容。我不是隐式事物的忠实拥护者...也许这是我需要克服的问题?
不,这是一个很好的习惯,也是一个好习惯。特别是,在C中发生了许多危险的隐式类型提升,显式类型强制转换可以防止这种情况。
但是,如果您想变得显式,则实际上必须对所发生的事情有深刻的了解在C线之间。除非您这样做,否则最终将导致更多的问题,而不是使用此编码实践解决的问题。另外,您所做的一些毫无意义的事情只会使代码混乱。
这是我对您的代码的评论:
不要定义自己的布尔类型。而是在stdint.h中使用标准的
bool
类型。(int) 0
此强制类型转换毫无意义,并且没有任何价值。整数文字始终为int
类型。枚举常量也始终是int
类型。register
是黑暗时代的过时关键字,当时编译器在优化代码方面一文不值。如今,现代的编译器在优化代码方面比您做的要好得多,因此让它完成工作。同样,关键字register
也会引起各种副作用:例如,您不能使用寄存器变量的地址。因此,请勿使用register
,因为从来没有理由这样做。该关键字只保留在语言中以向后兼容。类似地,通常最好让编译器在大多数情况下处理内联。因为虽然内联使代码更快,但也使可执行文件更大。在确定何时进行大小优化和何时进行速度优化时,编译器可能会比您更好。
即使每个语句后面只有一行代码,也始终在每个语句之后使用
{}
。跳过{}
是危险的做法,几乎肯定会早晚导致错误。您想要明确一些,但错过了一些实际上很重要的案例。
1 << offset
表示您对有符号整数进行了左移。假设32位整数,则1 << 31
的情况导致未定义的行为。通常,在99.9%的情况下,对有符号整数使用按位运算符毫无意义。如果要编写安全代码,请明确输入1u << offset
。由于上述原因,
*x |= (1 << offset);
将int
隐式存储到unsigned int
中,在某些情况下可能具有危险。也由于上述原因,则将
~
应用于带符号的int,这总是很危险的做法。您没有函数声明,只有定义。您还没有声明局部功能
static
,这是正确的做法是专用于特定模块。正如其他人评论说,命名布尔“值”和功能用它来存储值是令人困惑的。在这种情况下,
uint8_t
将是更合适的类型。通常,在进行位操作时,最好使用stdint.h中的类型。
评论
\ $ \ begingroup \ $
“在每个语句之后始终使用{}”->“在分支语句的每个部分之后始终使用{}”?
\ $ \ endgroup \ $
–马丁F
2014年3月29日,0:56
\ $ \ begingroup \ $
另外,我不理解其他人的评论,命名布尔“值”并使用它来存储值会造成混淆。 uint8_t会更合适。我了解Jamal的建议,但不了解您的建议。
\ $ \ endgroup \ $
–马丁F
2014年3月29日在1:25
\ $ \ begingroup \ $
“整数文字始终是int类型的。”我认为取决于范围。如果int是32位,则5000000000可能是long long而不是int。
\ $ \ endgroup \ $
–chux-恢复莫妮卡
2014年3月29日下午5:34
\ $ \ begingroup \ $
@martinf关于{}:在每次if,else,else if,for,while,do-while和switch语句之后。关于布尔值:按位算术通常使用整数完成。如果要告诉函数将位设置为1或0,则传递1或0比传递true / false更有意义。
\ $ \ endgroup \ $
–伦丁
2014年3月31日下午6:29
\ $ \ begingroup \ $
@chux确实。对于十六进制或八进制文字,它们甚至可以是无符号类型。
\ $ \ endgroup \ $
–伦丁
2014年3月31日下午6:42
#2 楼
我认为set_bit
可能会更好,例如:unsigned set_bit(unsigned x, unsigned offset, bool value)
{
return (value)
? x | (1 << offset)
: x & ~(1 << offset);
}
我不确定为什么会这样。我的原因是:
它更接近我以前看到的用于执行此类操作的宏
指针可能被滥用(例如,您可以将空指针传递到您的版本中)
编译器可能会发现更容易优化(例如,因为获取局部变量的地址并将其传递给子例程可能会使编译器担心别名)。
通过定义分别名为
set_bit
和clear_bit
的函数,可能会获得更好的性能(OTOH,当您将硬编码的value
传递给set_bit
时,编译器可能会优化差异)。我不认为
short
向offset
参数添加了任何值;短裤通常是效率较低的表现;甚至短值也无法满足您的需要(您可能希望offset
的值介于0到31或63之间)。#3 楼
删除inline
和register
关键字。尽管您个人希望完全掌握机器,但编译器仍将决定要做什么,然后做得更好。请记住,默认编译器优化设置为
-O0
。为了使此答案正常运行,还应该将最低编译器优化级别设置为-O3
(如果尚未设置)。#4 楼
value
不是bool
变量的有用名称;听起来更像是持有数字而不是条件。您是说要像isBitValue
一样吗?如果是显式的,则意味着要定义自己的
bool
类型,那么对于C语言中的玩具程序来说可能没问题。否则,应该使用<stdbool.h>
。 评论
\ $ \ begingroup \ $
也许是bitValue。
\ $ \ endgroup \ $
– ChristW
2014年3月28日在1:10
#5 楼
这是set_bit
函数的无分支实现:return x & ~(1 << offset) | (value << offset);
评论
\ $ \ begingroup \ $
您缺少括号。应该是[x&〜(1 << offset))
\ $ \ endgroup \ $
– Mindor先生
2014年3月28日在17:08
\ $ \ begingroup \ $
固定,谢谢。由于&优先于|,因此括号是可选的。
\ $ \ endgroup \ $
–伊夫·达乌斯特(Yves Daoust)
2014年3月28日在17:28
评论
如果要使用布尔值,请考虑使用实际布尔值数据类型。 #include