什么是CHAR_BIT?

dat*_*ili 86 c bit-manipulation

引用用于计算整数绝对值(abs)的代码而不分支来自http://graphics.stanford.edu/~seander/bithacks.html:

int v;           // we want to find the absolute value of v
unsigned int r;  // the result goes here 
int const mask = v >> sizeof(int) * CHAR_BIT - 1;

r = (v + mask) ^ mask;
Run Code Online (Sandbox Code Playgroud)

专利变化:

r = (v ^ mask) - mask;
Run Code Online (Sandbox Code Playgroud)

CHAR_BIT它是什么以及如何使用它?

Ara*_*raK 209

CHAR_BIT是的位数char.目前,几乎所有架构每字节使用8位,但并非总是如此.一些旧机器曾经有7位字节.

它可以在<limits.h>.

  • C需要`CHAR_BIT> = 8`并允许更大的值,这些DSP只有一个类型的大小,通常为32位.POSIX需要`CHAR_BIT == 8`.通常,您可以假设任何面向服务器或面向交互用户的多用户/多任务架构都有可能连接到互联网或与外界交换文本数据具有"CHAR_BIT == 8". (57认同)
  • @Jens Gustedt:请引用C99规范中的一节.在精确宽度整数类型中,C99规范说"这些类型是可选的".(7.18.1.1/3)但是,最小宽度和最快宽度类型是必需的. (21认同)
  • @caf:不,这是因为C99需要存在类型`int8_t`和`uint8_t`.因此存在一种宽度为8.由于`sizeof`任何类型必须与`sizeof char`兼容,实际上`sizeof int8_t`必须为1.所以`CHAR_BIT == 8`.我在这里写了一些关于这个观察的东西:https://gustedt.wordpress.com/2010/06/01/how-many-bits-has-a-byte/ (6认同)
  • @jamesdlin&caf:对不起,我搞砸了.是的,我提到的要求实际上来自POSIX for`stdint.h`.因此它是必需的,它也被标记为*ISO C标准*的扩展,而不是指该标准的特定版本.我的错. (3认同)
  • 某些DSP具有10个或更多位字节. (2认同)
  • @datodatuashvili 哪台机器有 7 位字节? (2认同)

plu*_*ash 6

尝试回答CHAR_BIT原始问题中的明确问题(什么是)和隐含问题(这是如何工作的)。


C 和 C++ 中的Achar表示 C 程序可以寻址的最小内存单元*。

CHAR_BIT在 C 和 C++ 中表示 a 中的位数char。由于对 char 类型的其他要求,它必须始终至少为 8。实际上,在所有现代通用计算机上,该值恰好是 8,但某些历史或专业系统可能具有更高的值。

Java 没有CHAR_BITor的等价物sizeof,也不需要它,因为 Java 中的所有基本类型都是固定大小的,并且对象的内部结构对程序员来说是不透明的。如果将此代码翻译为 Java,您可以简单地替换sizeof(int) * CHAR_BIT - 1为固定值 31。

在此特定代码中,它用于计算 中的位数int。请注意,此计算假设该int类型不包含任何填充位。

假设您的编译器选择对有符号数字的位移位进行符号扩展,并假设您的系统对负数使用 2 补码表示,这意味着mask正值或零值将为 0,负值将为 -1。

要对二进制补码取反,我们需要执行按位非运算,然后加一。同样,我们可以减一,然后按位求反。

再次假设二进制补码表示法 -1 由全 1 表示,因此异或与 -1 等效于按位求反。

因此,当 v 为零时,该数字保持不变,当 v 为 1 时,该数字被取反。

需要注意的是,C 和 C++ 中的有符号溢出是未定义的行为。因此,abs在最负值上使用此实现会导致未定义的行为。可以通过添加强制转换来修复此问题,以便以 unsigned int 计算程序的最后一行。

* 通常但不一定与硬件可以寻址的最小内存单元相同。一种实现可以潜在地将多个硬件可寻址存储器单元组合成一个程序可寻址存储器单元,或者将一个硬件可寻址存储器单元分割成多个程序可寻址存储器单元。


R..*_*R.. 2

您应该知道,此代码取决于有符号类型上右移位的实现定义行为。gcc 承诺始终提供合理的行为(符号位扩展),但 ISO C 允许实现对高位进行零填充。

解决这个问题的一种方法是:

#ifdef HAVE_SIGN_EXTENDING_BITSHIFT
int const mask = v >> sizeof(int) * CHAR_BIT - 1;
#else
int const mask = -((unsigned)v >> sizeof(int) * CHAR_BIT - 1);
#endif
Run Code Online (Sandbox Code Playgroud)

您的Makefileconfig.h可以HAVE_SIGN_EXTENDING_BITSHIFT根据您的平台在构建时定义。

  • 我不明白这如何成为一个可以接受的答案,因为它没有回答问题,尽管这是一个非常有趣的评论。 (141认同)
  • @Mauris:有人编辑了问题并将子问题提升到问题标题。诚然,最初的标题很糟糕,但OP的问题是关于所引用的位黑客代码是如何工作的,而“它不会,至少不能移植,这就是原因”是一个有用的答案。 (22认同)
  • 啊我懂了。遗憾的是,这个问题在“什么是`CHAR_BIT`?”*的 Google 搜索结果中出现得非常靠前,即使这不是最初的问题。:( 根据你的解释,我明白你为什么写这个答案,但对于后代来说,(a)删除你的答案并将其重写为问题的评论可能更有用,以便 @AraK 出现在顶部,或者(b) 编辑您的答案,使其回答问题的当前标题。 (13认同)
  • 我只是查看了这个问题的历史,原来的问题实际上并没有询问代码是如何工作的。编辑提出的问题是其中唯一实际的问题。 (5认同)