正确解释有符号与无符号

Correct interpretation signed vs unsigned

我从通信中接收到 2 个字节,然后我需要合并这 2 个值以获得 16 位值。

现在假设我希望收到200这个数字,那么这两个字符就是ab

char a=0x00;
char b=0xc8;
int  cw = (a << 8) | b ;
printf("cw= %d\n",cw);

合并后变量 cw 变为 -56 而不是 200

如果我用 unsigned char 改变 char 我得到正确的值 200 我该如何解决?我希望收到正数和负数,当然还有数字 200

您应该做的唯一更改是定义无符号的最低有效字节:

char a;
unsigned char b;
... // receive a and b from your communication
int cw = (a << 8) | b;
printf("cw = %d\n", cw);

arithmetic/logic 表达式应该可以工作,但要解释它为什么不溢出可能并不简单,因为它涉及将 charunsigned char 提升为 int(我猜你的系统是 16 位的)。

如果您希望代码可移植(即不假设 16 位 int 或特定平台的任何其他 属性),请使用定义大小的整数,并进行显式转换。

int8_t a;
uint8_t b;
... // receive a and b from your communication
int16_t cw = (int16_t)((int16_t)a << 8) | (int16_t)b;
printf("cw = %d\n", (int)cw);

但是这段代码的可读性较差,所以我不确定这里更便携有什么优势。

16 bit value.

只要使用正确的类型。

unsigned char a = 0x00;
unsigned char b = 0xc8;
int16_t cw = ((unsigned int)a << 8) | b;

C 标准基本上没有提供将 1 位移入或移出符号位置的方法(<< 唯一定义的情况是针对不会溢出的非负值)并且没有明确的方法将无符号值转换为负值(将超出范围的值转换为有符号整数类型是实现定义的)。

所以我们不应该使用轮班。然而,负值的乘法当然是有定义的,所以我们可以使用:

int8_t  a;
uint8_t b;
// Put code here to receive a and b by some method.
uint16_t cw = a*256 + b;

如果您必须从无符号类型重建有符号整数,那么一个选项是测试符号位并手动应用二进制补码:

unsigned char a, b;
// Put code here to receive a and b by some method.
int cw = (a & 0x7f) << 8 | b;  // Assemble the low 15 bits.
if (a & 0x80)
    cw += -128*256;            // If sign bit is set, adjust.

您还可以复制以下位:

unsigned char a, b;
// Put code here to receive a and b by some method.
int16_t cw;
memcpy(&cw, (uint16_t []) { (uint16_t) a << 8 | b }, sizeof cw);

(以上假定您的 16 位整数使用二进制补码。)