c - 为什么没有类型转换警告?
问题描述
我有以下 C 代码片段,在 Clang 中启用了 -Wconversion:
uint8_t num_ch = get_num_channels();
uint64_t test_mask = 1 << num_ch;
我希望这会引发某种警告,因为 1 是一个隐式的“signed int”,然后被分配给一个 uint64_t,但编译器没有抱怨:(
我错过了什么?
解决方案
为什么没有类型转换警告?
简短回答:由于您正在使用的 clang/LLVM 版本存在缺陷(有些人甚至称其为错误)。
讨论/解释/推理
该问题源于对位移表达式 的评估s << u
,其中s
是有符号整数类型并且u
是无符号类型。现在,根据这个 C11 草案标准(加粗我的):
6.5.7 位移位运算符
...
3 对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。
这意味着表达式的结果是有符号整数。从今以后,我应该指出,我正在使用(并假设)一个使用 32 位“基本”int
类型的平台。另请注意,没有后缀的整数常量是有符号类型。u
注意:虽然我无法访问 OP 的嵌入式系统,也无法访问本地 GCC 编译器,但我仍然可以使用最新版本的 Visual Studio 2019(16.7.2 - I '当我发现它是什么时,我会添加 clang 版本)。
以下代码确实会生成预期的警告(实际上是两个警告,如图所示):
#include <stdio.h>
#include <stdint.h>
int main()
{
uint64_t test_mask = 1 << (uint8_t)(31); // Warnings here, as expected.
printf("%016llX\n", test_mask);
return 0;
}
警告:
警告:有符号移位结果 (0x80000000) 设置移位表达式类型 ('int') 的符号位并变为负数 [-Wshift-sign-overflow]
警告:隐式转换更改符号:'int' 到 'uint64_t' unsigned long long') [-Wsign-conversion]
此外,printf
调用的输出是FFFFFFFF80000000
,这说明了为什么警告很重要:位移(在 32 位上int
)产生了一个负值,然后在提升为 64 位时对其进行符号扩展 - 因此“损坏' 预期/预期的结果(0000000080000000
如果文字1
简单地更改为1u
,从而避免符号扩展)。
然而!如果对代码进行了非常小的(有些人甚至可能会说是微不足道的)更改,则将位移的文字(常量)右侧操作数更改为完全等效的变量,如下面的代码所示,那么来自的警告clang-cl消失(但输出仍然“损坏” - 因此执行的操作是相同的)。
int main()
{
uint8_t num_ch = 31;
uint64_t test_mask = 1 << num_ch; // No warning here!
printf("%016llX\n", test_mask);
return 0;
}
可能不是最有帮助的答案——但除了向 LLVM 的人员报告问题之外,还能做些什么呢?我肯定会提交这样的报告,因为 OP 发布的代码可能会导致一些严重的问题,这些问题应该由启用了完整警告的编译器检测和指示(MSVC 甚至对第二个代码片段也会发出警告)。
推荐阅读
- c++ - 无序集
> 抛出“调用隐式删除的默认构造函数”错误 - javascript - 如何委派更改 select2
- sql - 如何为需要在多个级别映射的数据设计数据库?
- node.js - 无法读取请求正文,但可以打印
- django - Django 是否将压缩目录保存为文件或这里可能发生什么?
- npm-install - Angular 提供的 es5BrowserSupport
- php - PHP复选框值到MYSQL无限循环问题
- amazon-ec2 - 当计数大于 1 时如何使用 ansible 提取 ec2 实例启动的单个 ip
- spring-boot-admin - 找不到与本地主机匹配的名称
- javascript - 如何从打字稿文件打开引导弹出模型