4

我有两个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) 为什么?
(但是:c1并且c2在我的真实应用程序中是负数......也许这是问题的一部分?)

所以,我的第二个解决方案是使用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) 最好/最快的方法是什么?

谢谢!

4

5 回答 5

7

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

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

1111 1011

如果你把它转换成intor even unsigned int,它就变成了

1111 1111 1111 … 1111 1011

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

为了解决这个问题,在移位之前将chars 转换为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;
}
于 2010-08-15T10:36:51.443 回答
3

$5.8/1 声明-“操作数应为整数或枚举类型,并执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数为负数或大于或等于,则行为未定义到提升的左​​操作数的位长度。”

因此,尝试将 c1 类型转换为 unsigned int,然后与 C2 进行按位或。还将输出作为无符号整数返回。chars 被提升为 int 但我们希望成为 'unsigned int'

于 2010-08-15T10:23:25.320 回答
2

原因是在执行按位或之前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(如果 short 是 2 个字节长,则c1符号扩展超出这 2 个字节并不重要)

short int stitch(char c1, char c2) {
    return ((unsigned char)c1 << 8) | (unsigned char)c2;
}
于 2010-08-15T10:35:41.680 回答
1

移位/或方法一旦固定,就会更干净,因为它不依赖于字节顺序。

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

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

于 2010-08-15T11:13:44.100 回答
-1

你不应该为此使用联合。你不应该同时使用联合字段。如果工会有成员 A 和成员 B,那么您必须认为 A 和 B 是不相关的。那是因为编译器可以在任何它想要的地方自由添加填充(除了结构的前面)。另一个问题是字节顺序(小/大端)。

//EDIT 上面的“联合规则”有一个例外,您可以同时使用这些位于前部且具有相同布局的成员。IE

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

Ac 和 Ai 可以与 Bc 和 Bi 同时使用

于 2010-08-15T10:40:44.370 回答