C ++:Union vs Bitwise运算符

时间:2010-08-15 10:19:33

标签: c++ performance bitwise-operators unions

我有两个char,我希望将它们“按比例”拼接在一起 例如:

char c1 = 11; // 0000 1011
char c2 = 5;  // 0000 0101
short int si = stitch(c1, c2); // 0000 1011 0000 0101

所以,我首先尝试的是按位运算符:

short int stitch(char c1, char c2)
{
    return (c1 << 8) | c2;
}

但这不起作用:我的short等于c2 ... (1)为什么?
(但是:c1c2在我的真实应用中是负数...也许这是问题的一部分?)

所以,我的第二个解决方案是使用union

union stUnion
{
    struct
    {
         char c1;
         char c2;
    }
    short int si;
}

short int stitch(char c1, char c2)
{
    stUnion u;
    u.c1 = c1;
    u.c2 = c2;
    return u.si;
}

这就像我想要的那样... 我认为

(2)什么是最好/最快的方式?

谢谢!

5 个答案:

答案 0 :(得分:7)

union方法最多是实现定义的(实际上,它将非常可靠地工作,但si的格式取决于平台的字节顺序。)

正如您所怀疑的那样,按位方式的问题是负数。负数由前导1的链表示。所以-5例如是

1111 1011

如果将其投放到int甚至unsigned int,则会变为

1111 1111 1111 … 1111 1011

,当应用OR时,所有这些1将淹没左移数据。

要解决此问题,请先将char转换为unsigned char,然后转移到int(以防止溢出,甚至出现溢出的可能性):

short int stitch(char c1, char c2)
{
    return ( (int) (unsigned char) c1 << 8) | (unsigned char) c2;
}

或者,如果您可以自由更改参数的类型,并且可以包含<cstdint>

uint16_t stitch( uint8_t c1, uint8_t c2)
{
    return ( (int) c1 << 8 ) | c2;
}

答案 1 :(得分:3)

$ 5.8 / 1状态 - “操作数应为整数或枚举类型,并执行整数提升。结果的类型是提升左操作数的类型。如果右操作数为负数,则表示行为未定义大于或等于提升左操作数的位数。“

因此,尝试将c1强制转换为unsigned int,然后将其与C2进行按位OR运算。还将输出作为unsigned int返回。 chars被提升为int但我们希望成为'unsigned int'

答案 2 :(得分:2)

原因是在执行按位OR之前c2首先被提升为int,这会导致符号扩展(假设char已签名且可以保留负值):

char x1 = -2; // 1111 1110
char x2 = -3; // 1111 1101

short int si = stitch(c1, c2); // 1111 1111 1111 1101

x2提升为int的表示(至少)1字节满1,因此它会覆盖您之前向上移位的x1的零位。您可以先转换为unsigned char。使用两个补码表示,不会更改最低字节中的位模式。虽然不是绝对必要,但您也可以将c1强制转换为unsigned char以保持一致性(如果短2个字节长,则c1符号扩展超出这2个字节无关紧要)

short int stitch(char c1, char c2) {
    return ((unsigned char)c1 << 8) | (unsigned char)c2;
}

答案 3 :(得分:1)

移位/或方法一旦修复,就更清晰,因为它不依赖于字节顺序。

除此之外,由于存储到转发(STLF)问题,许多现代CPU上的union方法可能会更慢。您正在将值写入内存,然后将其作为不同的数据类型读回。如果发生这种情况,许多CPU无法快速将数据发送到负载。负载需要等到存储完全完成(退役),将其数据写入L1缓存。

在没有桶形移位器的非常旧的CPU上(移位8需要8次操作)并且通过简单的有序执行,例如68000,union方法可能更快。

答案 4 :(得分:-1)

你不应该使用联盟。你永远不应该同时使用联合字段。如果工会有成员A和成员B,那么你必须考虑A和B是无关的。那是因为编译器可以随意在任何地方添加填充(除了在struct的前面)。另一个问题是字节顺序(小/大端)。

// EDIT 上面的“联合规则”有例外,您可以同时使用这些成员,这些成员在前面并具有相同的布局。即

union {
    struct {
        char c;
        int i;
        short s;
    } A;
    struct {
        char c;
        int i;
        char c1;
        char c2;
    } B;
};

A.c和A.i可与B.c和B.i同时使用