将要存储在 int 中的左移 short 是否未定义?

Is left shifted short that is going to be stored in an int is UNDEFINED?

我正在阅读一本实现自己动手的 DNS 消息的书 reader,它会尝试查看某个特定字段是否设置为真。 书中使用的一段代码我看不太懂

const int qdcount = (msg[10] << 8) + msg[11];

msg 是 ==> char 类型(即 8 位)

qdcount ==> 应该是一个 16 位的字段,包含 DNS 查询的数量(由 2 个字段组成 msg[10]msg[11]

那么这段代码是如何工作的(例如,如果 msg[10] = 01001 0001)将其左移 8 应该会产生 (1000 0000),即 UB,那么完成的任何计算都会导致错误的答案。假设msg[11] = 0010 1111。计算结果是 1000 0000 + 0010 1111 对吧?。那么这行代码是如何工作的。

来自 C11 – ISO/IEC 9899:2011 draft paragraph 6.5.7.4 version linked here.

[...] E1 << E2 [...]
If E1 has a signed type and nonnegative value, and E1 × 2^E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

是的。左移带符号的值可能会导致未定义的行为。

您代码中的另一件事实际上导致您感到困惑:

The integer promotions are performed on each of the operands.

这意味着在移位之前,您的操作数会扩展到 int。因此,通过将位移入符号位(假设 sizeof(int)>16),移位 8 不会导致溢出。

至少有 2 个独立的考虑因素。

  • char有符号无符号?

  • int 16 位或更宽?

    (msg[10] << 8) + msg[11];
    

大部分问题取决于:

The result of E1 << E2 is E1 left-shifted E2 bit positions ...
If E1 has a signed type and nonnegative value, and E1 *2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

char 已签名int 任意大小

msg[10] 被提升为 int 并左移 8。当值 msg[10] 为正时这很好,而当负值为负时未定义的行为,因为左移负值是 UB。

charunsignedint 比 16 位宽

msg[10] 提升为 int 并向左移动 8。这很好,所有 msg[10] 值都没有问题。

char无符号int是16位

msg[10] 提升为 int 并左移 8。当 msg[10] < 128 时这很好,否则是 UB 移入符号位置 - 正值不可表示。


移位时最好使用无符号类型。

// char msg[100];
// const int qdcount = (msg[10] << 8) + msg[11];

char unsigned msg[100];
const unsigned qdcount = ((unsigned) msg[10] << 8) + msg[11];