按位AND和签名字符

Ric*_*ard 4 c bit-manipulation

我有一个文件,我已经读入数据类型的数组signed char.我不能改变这个事实.

我现在想这样做:!((c[i] & 0xc0) & 0x80)其中c[i]一个是签名字符.

现在,我从C99标准的 6.5.10节知道"每个操作数[按位AND]应该具有整数类型."

C99规范的第6.5节告诉我:

一些运算符(一元运算符〜,以及二元运算符<<,>>,&,^和|,统称为按位运算符)应具有具有整数类型的操作数.这些运算符返回依赖于整数内部表示的值,因此具有签名类型的实现定义方面.

我的问题是双重的:

  • 由于我想使用文件中的原始位模式,我如何转换/转换signed charunsigned char使得位模式保持不变?

  • 是否存在这些"实现定义方面"的列表(例如MVSC和GCC)?

或者你可以采取不同的路径,并认为这对于任何值的有符号和无符号字符产生相同的结果c[i].

当然,我会奖励对相关标准或权威文本的引用,并阻止"知情"的推测.

Jas*_*onD 5

正如其他人所指出的那样,在所有可能的情况下,您的实现都是基于两个补码,并且会给出您期望的结果.

但是,如果您担心涉及有符号值的操作的结果,并且您关心的只是位模式,则只需直接转换为等效的无符号类型.结果在标准下定义:


6.3.1.3有符号和无符号整数

  1. ...

  2. 否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内.


这实质上是指定结果将是值的二进制补码表示.

对此的基本原理是,在二进制补码数学中,计算结果以两个幂的模数(即类型中的位数)为模,这反过来相当于屏蔽相关的位数.而数字的补码是从2的幂中减去的数字.

因此,添加负值与添加任何值的值相同,该值与值的差值乘以2的幂的倍数.

即:

        (0 + signed_value) mod (2^N)
==
      (2^N + signed_value) mod (2^N)
==
  (7 * 2^N + signed_value) mod (2^N)
Run Code Online (Sandbox Code Playgroud)

等等(如果你知道模数,这应该是非常明显的真实)

因此,如果你有一个负数,增加2的幂将使其为正(-5 + 256 = 251),但是底部的'N'位将完全相同(0b11111011)并且它不会影响a的结果数学运算.因为值被截断以适合类型,结果就是你期望的二进制值,即使结果"溢出"(即,如果数字为正数,你可能认为会发生这种情况 - 这种包装也是明确定义的行为).

所以在8位二进制补码中:

  • -5与251(即256-5)-0b11111011相同
  • 如果添加30和251,则得到281.但是大于256,而281 mod 256等于25.与30 - 5完全相同.
  • 251*2 = 502. 502 mod 256 = 246. 246和-10均为0b11110110.

同样,如果你有:

unsigned int a;
int b;

a - b == a + (unsigned int) -b;
Run Code Online (Sandbox Code Playgroud)

在引擎盖下,这种转换不太可能通过算术实现,并且肯定是从一个寄存器/值到另一个的直接分配,或者只是完全优化,因为数学不区分有符号和无符号(CPU标记的解释)是另一回事,但这是一个实现细节).该标准的存在是为了确保一个实现不会自己做一些奇怪的事情,或者我想,对于一些不使用二进制补码的奇怪架构......