这对我来说是一个真正的WTF,看起来像GCC中的一个错误,但我想让社区看一看并为我找到解决方案.
这是我能想到的最简单的程序:
#include <stdio.h>
#include <stdint.h>
int main(void)
{
uint16_t i = 1;
uint16_t j = 2;
i += j;
return i;
}
Run Code Online (Sandbox Code Playgroud)
我正在尝试使用-Werror=conversion标志在GCC上编译它,我正在使用我的大部分代码.
这是结果:
.code.tio.c: In function ‘main’:
.code.tio.c:9:7: error: conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value [-Werror=conversion]
i += j;
Run Code Online (Sandbox Code Playgroud)
此代码会发生同样的错误:
uint16_t i = 1;
i += ((uint16_t)3);
Run Code Online (Sandbox Code Playgroud)
错误是
.code.tio.c: In function ‘main’:
.code.tio.c:7:7: error: conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value [-Werror=conversion]
i += ((uint16_t)3);
^
Run Code Online (Sandbox Code Playgroud)
为了清楚起见,这里的错误在于+=操作员,而不是演员.
它看起来像运算符重载为+=与uint16_t被搞砸.或者我错过了一些微妙的东西?
供您使用:MCVE
编辑:更多相同:
.code.tio.c:8:6: error: conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value [-Werror=conversion]
i = i + ((uint16_t)3);
Run Code Online (Sandbox Code Playgroud)
但i = (uint16_t)(i +3);至少有效......
dbu*_*ush 33
隐式转换的原因是+=运算符与=和的等价性+.
根据C标准第6.5.16.2节:
3形式E1 op = E2的复合赋值等效于简单赋值表达式E1 = E1 op(E2),除了左值E1仅被评估一次,并且对于不确定序列的函数调用,操作复合赋值是单一评估
所以这:
i += ((uint16_t)3);
Run Code Online (Sandbox Code Playgroud)
相当于:
i = i + ((uint16_t)3);
Run Code Online (Sandbox Code Playgroud)
在此表达式中,运算+符的操作数被提升为int,并将其int分配回a uint16_t.
第6.3.1.1节详细说明了原因:
2以下内容可用于任何地方
int或unsigned int可能使用的表达方式:
- 一个整型(比其它的对象或表达
int或unsigned int),其整数转换秩小于或等于的秩int和unsigned int.- 类型的位字段
_Bool,int,signed int,或unsigned int.如果a
int可以表示原始类型的所有值(由宽度限制,对于位字段),则该值将转换为int; 否则,它被转换为unsigned int.这些被称为 整数促销.整数促销不会更改所有其他类型.
因为a uint16_t(aka a unsigned short int)的排名低于int,所以当用作操作数时,值会被提升+.
您可以通过分解+=操作员并施放右侧来解决这个问题.此外,由于促销,对值3的强制转换无效,因此可以删除:
i = (uint16_t)(i + 3);
Run Code Online (Sandbox Code Playgroud)
但请注意,此操作会发生溢出,这是没有强制转换时发出警告的原因之一.例如,如果i值为65535,则i + 3类型int和值为65538.当结果返回到时uint16_t,从该值中减去值65536,得到值2,然后将其分配回i.
在这种情况下,此行为已明确定义,因为目标类型是无符号的.如果目标类型已签名,则结果将是实现定义的.
小智 13
任何算术运算符的参数都遵循N1570(最新的C11草案),§6.3.1.8中描述的通常的算术转换.与此问题相关的段落如下:
[关于浮点类型的一些规则]
否则,将对两个操作数执行整数提升.
因此,进一步查看整数促销的定义方式,我们在第6.3.1.1节中找到相关文本:
如果a
int可以表示原始类型的所有值(由宽度限制,对于位字段),则该值将转换为int; 否则,它被转换为unsigned int.这些被称为整数促销.
所以,即使使用此代码:
i += ((uint16_t)3);
Run Code Online (Sandbox Code Playgroud)
算术运算符的存在会导致操作数转换回int.作为分配是操作的一部分,它分配一个int到i.
这确实是相关的,因为i + 3实际上可能会溢出uint16_t.
Lun*_*din 11
i += ((uint16_t)3);
Run Code Online (Sandbox Code Playgroud)
等于(1)
i = i + ((uint16_t)3);
Run Code Online (Sandbox Code Playgroud)
最右边的操作数显式地从int(整数常量的类型3)转换为强制转换uint16_t.之后,通常的算术转换(2)应用于两个操作数+,之后两个操作数被隐式转换为int.操作的结果+是类型int.
然后,您尝试将存储int在uint16_t其中正确导致从一个警告-Wconversion.
如果你想避免分配一个int可能的解决方案uint16_t将是这样的(符合MISRA-C等):
i = (uint16_t)(i + 3u);
Run Code Online (Sandbox Code Playgroud)
(1)这是所有复合赋值算子的要求,C11 6.5.16.2:
阿化合物分配形式的E1 OP = E2相当于简单赋值表达式E1 = E1 OP(E2),不同之处在于左值E1仅计算一次,