带按位或和转换操作数的转换警告

Conversion Warning with Bitwise Or and Casted Operands

代码片段 1(如下所示)产生以下 -Wconversion 警告:

debug_Wconversion.c:10:57: warning: conversion to ‘uint16_t’ from ‘int’ may alter its value [-Wconversion]
     result = ((uint16_t) (((uint16_t) byte1) & 0x0050)) | ((uint16_t) byte2);
                                                         ^

代码片段 2 和 3 不产生 -Wconversion 警告。

代码片段 1:

uint8_t  byte1 = 0xF0;
uint8_t  byte2 = 0x0F;
uint16_t result;
result = ((uint16_t) (((uint16_t) byte1) & 0x0050)) | ((uint16_t) byte2);

代码片段 2:

uint8_t  byte1 = 0xF0;
uint8_t  byte2 = 0x0F;
uint16_t result;
uint16_t leftOrOperand;
uint16_t rightOrOperand;
leftOrOperand  = ((uint16_t) (((uint16_t) byte1) & 0x0050));
rightOrOperand = ((uint16_t) byte2);
result = leftOrOperand | rightOrOperand;

代码片段 3:

uint8_t  byte1 = 0xF0;
uint8_t  byte2 = 0x0F;
uint16_t result;
result = (uint16_t) (((uint16_t) (((uint16_t) byte1) & 0x0050)) | ((uint16_t) byte2));

为什么第一个代码片段会产生警告,而其他代码片段却不会?具体是什么导致了警告?

我觉得这很费解。第一个片段中的警告暗示整数提升发生在按位或操作中,尽管操作数是显式转换的。第二个片段演示了按位或运算本身不会导致整数提升。如果保留操作顺序,第三个代码片段执行与第一个代码片段相同的操作,但在分配结果变量之前转换按位或的结果。关于强制转换和按位或运算之间的相互作用,我显然有一些不明白的地方。任何有助于澄清我的理解的帮助将不胜感激!

其他可能相关的信息:

提前致谢!

按位或运算符 | 和按位与运算符 & 都对两个操作数执行整数提升。其中一个操作数是强制转换的结果这一事实并没有改变这一点。

-Wconversion 标志对于它所警告的内容往往有点过分热情。给定 32 位 int,从 uint16_tint 的转换将 不会 改变它的值。

C 标准确实指定 int 的最小范围是 -32767 到 32767,因此具有此限制的实现可能会看到值的变化,尽管您很难找到在这种情况下运行 gcc 的系统。

但是这个特殊的警告被强制转换消音了。来自手册页:

-Wconversion

Warn for implicit conversions that may alter a value. This includes conversions between real and integer, like "abs (x)" when "x" is "double"; conversions between signed and unsigned, like "unsigned ui = -1"; and conversions to smaller types, like "sqrtf (M_PI)". Do not warn for explicit casts like "abs ((int) x)" and "ui = (unsigned) -1", or if the value is not changed by the conversion like in "abs (2.0)". Warnings about conversions between signed and unsigned integers can be disabled by using -Wno-sign-conversion.

For C++, also warn for confusing overload resolution for user-defined conversions; and conversions that never use a type conversion operator: conversions to "void", the same type, a base class or a reference to them. Warnings about conversions between signed and unsigned integers are disabled by default in C++ unless -Wsign-conversion is explicitly enabled.

Why does the first code snippet produce the warning, but the others do not? What specifically is causing the warning?

((uint16_t) (((uint16_t) byte1) & 0x0050)) | ((uint16_t) byte2) 是一个 int 被分配给一个 uint16_t.

((uint16_t) (((uint16_t) byte1) & 0x0050))uint16_t.
((uint16_t) byte2) 也是一个 uint16_t.
uint16_t | uint16_t 首先在 |.

之前将操作数提升为 int

代码正在将 int 分配给 uint16_t - 因此出现警告。

在第二个片段中,编译器更好地理解了两个 uint16_t 的或运算(结果也是 int)不会导致下转换为 uint16_t 的转换问题。第一个只是有点太复杂了,编译器无法推断出任何问题。

在 3 号中,最后的演员阵容消除了任何向下转换问题。


备选方案:使用 unsigned 常量:0x0050uunsigned 数学 unsigned 类型,例如 uint16_t.

uint8_t  byte1 = 0xF0u;
uint8_t  byte2 = 0x0Fu;
//                 v-------------v unsigned result
//                         v-----v unsigned constant
uint16_t result = (byte1 & 0x0050u) | byte2;
//                ^-----------------------^ unsigned result
//       ^--------------------------------^ initializing: unsigned to uint16_t

一个讨厌的编译器可能仍然抱怨缩小初始化。

// Non-cast alternative
uint16_t result = UINT16_MAX & ((byte1 & 0x0050u) | byte2);
// Cast alternative
uint16_t result = (uint16_t)   ((byte1 & 0x0050u) | byte2);