为什么对(超过)三个无符号字符的算术运算在使用 -Wconversion 时总是触发警告?
Why does arithmetic operations on (more than) three unsigned chars always trigger a warning when using -Wconversion?
编译以下代码
// foo.c
typedef unsigned char uint8_t;
int main() {
uint8_t a = 1;
uint8_t b = 10;
uint8_t c = 12;
uint8_t k = a + b + c;
}
使用 GCC 11.2(还有其他 GCC 版本)和以下完整命令:
gcc -Wconversion foo.c
导致以下警告:
Output of x86-64 gcc 11.2 (Compiler #3)
<source>: In function 'main':
<source>:11:17: warning: conversion from 'int' to 'uint8_t' {aka 'unsigned char'} may change value [-Wconversion]
11 | uint8_t k = a + b + c;
| ^
Compiler returned: 0
例如,添加两个无符号字符时不会发生这种情况,即 a + b
。
当在三个以上的无符号字符之间使用算术运算的任意组合时,就会发生这种情况。
clang 版本 12.0.1 不会发生这种情况。
例如,为什么 a + b + c
导致 int
在分配给 k
之前必须转换为 uint8_t
以抑制警告?
这是由于整数提升在 C 中的工作方式。C 标准指定如果 int
可以保存 a
和 b
类型的值 - 那么它们转换为 int
进行操作。
uint8_t
和int
就是这种情况。
引自标准 (6.3.1.1):
Every integer type has an integer conversion rank defined as follows
...
If an int can represent all values of the original type, the value is
converted to an int; otherwise, it is converted to an unsigned int.
These are called the integer promotions. 48) All other types are
unchanged by the integer promotions
在旧版本的 gcc 中 - 这个警告 是 由添加两个操作数如 a + b
触发的。在 GCC 10 中,此警告已默认从 -Wconversion
中删除,但此警告在 -Warth-conversion
中可用。来自 GCC 10 release notes:
-Warith-conversion re-enables warnings from -Wconversion, -Wfloat-conversion, and -Wsign-conversion that are now off by default for an expression where the result of an arithmetic operation will not
fit in the target type due to promotion, but the operands of the
expression do fit in the target type.
即由于 GCC 10 a + b
不会触发 -Wconversion
警告的原因是表达式的两个操作数都是 uint8_t
,它们符合目标类型(int
).
有了 a + b + c
就不再是这种情况了,因为 a + b
变成了 int
,表达式变成了 int + uint8_t
- 这似乎不满足标准“表达式的操作数确实符合目标类型”。
编译以下代码
// foo.c
typedef unsigned char uint8_t;
int main() {
uint8_t a = 1;
uint8_t b = 10;
uint8_t c = 12;
uint8_t k = a + b + c;
}
使用 GCC 11.2(还有其他 GCC 版本)和以下完整命令:
gcc -Wconversion foo.c
导致以下警告:
Output of x86-64 gcc 11.2 (Compiler #3)
<source>: In function 'main':
<source>:11:17: warning: conversion from 'int' to 'uint8_t' {aka 'unsigned char'} may change value [-Wconversion]
11 | uint8_t k = a + b + c;
| ^
Compiler returned: 0
例如,添加两个无符号字符时不会发生这种情况,即 a + b
。
当在三个以上的无符号字符之间使用算术运算的任意组合时,就会发生这种情况。
clang 版本 12.0.1 不会发生这种情况。
例如,为什么 a + b + c
导致 int
在分配给 k
之前必须转换为 uint8_t
以抑制警告?
这是由于整数提升在 C 中的工作方式。C 标准指定如果 int
可以保存 a
和 b
类型的值 - 那么它们转换为 int
进行操作。
uint8_t
和int
就是这种情况。
引自标准 (6.3.1.1):
Every integer type has an integer conversion rank defined as follows
...
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. 48) All other types are unchanged by the integer promotions
在旧版本的 gcc 中 - 这个警告 是 由添加两个操作数如 a + b
触发的。在 GCC 10 中,此警告已默认从 -Wconversion
中删除,但此警告在 -Warth-conversion
中可用。来自 GCC 10 release notes:
-Warith-conversion re-enables warnings from -Wconversion, -Wfloat-conversion, and -Wsign-conversion that are now off by default for an expression where the result of an arithmetic operation will not fit in the target type due to promotion, but the operands of the expression do fit in the target type.
即由于 GCC 10 a + b
不会触发 -Wconversion
警告的原因是表达式的两个操作数都是 uint8_t
,它们符合目标类型(int
).
有了 a + b + c
就不再是这种情况了,因为 a + b
变成了 int
,表达式变成了 int + uint8_t
- 这似乎不满足标准“表达式的操作数确实符合目标类型”。