如何以C标准的方式进行位表示?

如何以C标准的方式进行位表示?,c,bit-manipulation,standards,bit,C,Bit Manipulation,Standards,Bit,根据C标准,整数类型的值表示是实现定义的。因此,5可能不会像我们通常在32位2的补码中假设的那样,表示为0000000000000000000101或-1表示为11111111111111111。因此,即使操作符~、定义良好,它们将处理的位模式也是实现定义的。我能找到的唯一定义的位模式是“§5.2.1/3所有位设置为0的字节(称为空字符)应存在于基本执行字符集中;它用于终止字符串。” 所以我的问题是-是否有一种独立于实现的方式将整数类型转换为位模式? 我们总是可以从一个空字符开始,并对其进行足够

根据C标准,整数类型的值表示是实现定义的。因此,
5
可能不会像我们通常在32位2的补码中假设的那样,表示为
0000000000000000000101
-1
表示为
11111111111111111
。因此,即使操作符
~
定义良好,它们将处理的位模式也是实现定义的。我能找到的唯一定义的位模式是“§5.2.1/3所有位设置为0的字节(称为空字符)应存在于基本执行字符集中;它用于终止字符串。”

所以我的问题是-是否有一种独立于实现的方式将整数类型转换为位模式?


我们总是可以从一个空字符开始,并对其进行足够的位操作,以使其达到所需的值,但我发现它太麻烦了。我还意识到,几乎所有的实现都将使用2的补码表示,但我想知道如何用纯C标准的方式来实现。就我个人而言,由于设备驱动程序编程的问题,我觉得这个话题非常有趣,因为迄今为止编写的所有代码都假定一个特定的实现。

如果你想得到给定的
int
的位模式,那么位运算符就是你的朋友。如果您想将
int
转换为它的2-补表示,那么算术运算符是您的朋友。这两种表示形式可以不同,因为它是由实现定义的:

标准草案2011。6.5/4.一些运算符(一元运算符~,和 二进制运算符&、^和|,统称为 位运算符)的操作数必须为整数 类型。这些运算符生成的值取决于内部 整数的表示形式,并定义了实现和 签名类型的未定义方面

因此,这意味着
i一般来说,在大多数情况下适应不寻常的平台并不难(如果你不想简单地假设8位
char
,2的补码,没有填充,没有陷阱,以及截断无符号到有符号的转换),该标准提供了足够的保证(不过,使用一些宏来检查某些实现细节会有所帮助)

就严格一致性程序所能观察到的(位字段外)而言,5始终被编码为
00…0101
。这不一定是物理表示(无论这意味着什么),而是可移植代码所能观察到的。例如,在内部使用格雷码的机器必须模拟“纯二进制表示法”用于按位运算符和移位

对于有符号类型的负值,允许使用不同的编码,这会导致在重新解释为相应的无符号类型时产生不同的(但对每种情况都有明确定义)结果。例如,严格一致的代码必须区分
(无符号)n
*(无符号*)&n
对于有符号整数
n
:如果
n
为负数,则它们对于两个不带填充位的补码是相等的,但对于其他编码则不同

此外,可能存在填充位,有符号整数类型的填充位可能比相应的无符号类型多(但不是相反,从有符号到无符号的类型双关总是有效的).
sizeof
不能用于获取非填充位的数量,因此,例如,要获取仅设置了符号位(对应的符号类型)的无符号值,必须使用类似以下内容:

#define TYPE_PUN(to, from, x) ( *(to *)&(from){(x)} )
unsigned sign_bit = TYPE_PUN(unsigned, int, INT_MIN) &
                    TYPE_PUN(unsigned, int, -1) & ~1u;
(可能有更好的方法)而不是

unsigned sign_bit = 1u << sizeof sign_bit * CHAR_BIT - 1;
print\u bits
根据所用的表示法(它给出了原始位模式)对负数给出不同的结果,
print\u bits\u 2comp
给出了两个位的补码表示法(如果
无符号int
的填充位较少,则宽度可能大于
有符号int
的宽度)

在使用位运算符时,以及在从无符号到有符号的类型双关时,必须注意不要生成陷阱表示,请参见下面的潜在生成方式(例如,
*(int*)和sign_bit
可以使用二的补码进行陷阱,而
-1 | 1
可以使用一的补码进行陷阱)

无符号到有符号整数的转换(如果转换后的值在目标类型中不可表示)总是由实现定义的,我认为非2的补码机器更有可能与通用定义不同,尽管从技术上讲,它也可能成为2的补码实现的一个问题

根据C11(n1570)6.2.6.2:

(1) 对于除
无符号字符
以外的无符号整数类型,对象表示的位应分为两组:值位和填充位(不需要任何后者)。如果存在N值位,则每个位应表示12N-1之间的不同2次幂,以便该类型的对象应能够使用纯二进制表示法表示从02N-1的值;这称为值表示法。未指定任何填充位的值

(二)对于有符号整数类型,对象表示的位应分为三组:值位、填充位和符号位。无需任何填充位;
有符号字符
不应具有任何填充位。应仅具有一个符号位。作为值位的每一位应具有与中相同位相同的值对应无符号类型的对象表示形式(如果有符号类型中有M值位) 在无符号类型中键入并N,然后键入M≤N)。如果符号位为零,则不会影响
int print_bits_u(unsigned n) {
    for(; n; n>>=1) {
        putchar(n&1 ? '1' : '0'); // n&1 never traps
    }
    return 0;
}

int print_bits(int n) {
    return print_bits_u(*(unsigned *)&n & INT_MAX);
    /* This masks padding bits if int has more of them than unsigned int.
     * Note that INT_MAX is promoted to unsigned int here. */
}

int print_bits_2scomp(int n) {
    return print_bits_u(n);
}