c - GCC 警告没有强制转换,而 MSVC 警告有强制转换,为什么?
问题描述
我有一个 C 库,它允许用户#define
使用整数类型并生成一个专门用于该整数类型的函数,就像 C++ 模板函数一样。
#define THE_INTTYPE signed short
该函数需要知道可由 表示的最小值THE_INTTYPE
。为了用户的方便,我不要求他们这样#define
做。相反,我设置了最重要的位来获取它。
typedef THE_INTTYPE rInt;
enum { /* assume */ char_bit = 8 };
rInt const rMin = ((rInt)1 << (sizeof(rInt) * char_bit - 1));
此时,警告级别 4 的 MSVC2017 不发出警告,但gcc -pedantic -Wall
发出warning: overflow in implicit constant conversion [-Woverflow]
. 我明白为什么它会抱怨。这是因为我已经跨过了有符号整数的 MSB。
哇,整数值从正(移位前)隐式转换为负(移位后)!似乎是个错误,最好警告他。- 海合会认为
我通过添加 cast 来断言我的意图,(rInt)
所以它变成了(rInt)((rInt)1 <<...
. GCC 不再抱怨。然而,MSVC2017 现在出人意料地给出了警告:warning C4310: cast truncates constant value
.
最后,我设法通过强制转换来“修复”1
它uintmax_t
。
/* Perfect no warning code */
rInt const rMin = (rInt)((uintmax_t)1 << (sizeof(rInt) * char_bit - 1));
问题是,为什么?如果 MSVC2017 比 GCC 更严格,为什么当且仅当我添加演员表时它才会发出警告?为什么 MSVC2017 在我添加演员表后才决定发出警告?我在这里错过了什么吗?
((signed short)1) -> 0000 0000 0000 0001
<< (2 * 8 - 1) -> 1000 0000 0000 0000 // GCC warns, understandable, MSVC no warn
(signed short)((signed short)1 ... // GCC no warn, MSVC warns, why?
解决方案
So I went to read the official documentation of C4310 warning once again, and found the keyword: cast and truncate.
Apparently MSVC thinks the intentional casting is unintentional truncating. How so? Let's see the official example.
long int a;
a = (char) 128; // C4310, use value 0-127 to resolve
For MSVC, char
is signed by default. Since a signed char
ranges from -128 to 127, when the compiler saw someone trying to cast 128
as char
which will surely end up as totally different value -128
, the compiler will emit the warning C4310, because the casting seems like a mistake.
However, if you remove the cast, and directly assign the value to a char
instead:
char c = 128; // warning?
You may think that, since MSVC smartly warns programmer about questionable casting, for sure it will emit a bigger warning on this one. Unfortunately, it would not. I repeat, it would not emit any warning.
So if you make a mistake, initialize signed
variable with positive integer value that can only be represent by unsigned
, MSVC will not warn you. MSVC will warn you only if you initialize with a value that even the unsigned
version of the data type cannot handle.
char c = 255; // no warning
char c = 256; // warning: truncation from 'int' to 'char'
Or if you really want MSVC to check that for you, you need to cast.
char c = (char)128; // MSVC emits C4310
As such, I find C4310 unhelpful and possibly harmful. It seems to encourage C programmer not to cast things that should be cast. Because if you cast, it warns. If you no cast, it will not warn. So why cast when you can get away without casting? This is ridiculous IMHO.
In GCC, the situation reverses. Cast? No warning. No cast? Emit warning. It is just that simple.
As such, if you are trying to writing similar code that compiles without warning under both compiler, you are in a checkmate situation, as this question's title states, GCC warns without cast, while MSVC warns with cast. Welcome to the compiler-incompatible-with-each-other territory.
Bouns
Find minimum value that can be hold by an arbitrary signed integer type without warning from both compiler, without uintmax_t
.
typedef short rInt;
rInt const rIntMin = (rInt)-1 << (sizeof(rInt) * CHAR_BIT - 1);
How it works? Take 16-bit short for example, the code turns 1111 1111 1111 1111
to 1000 0000 0000 0000
. Since the integral value is negative before bit-shift, and also negative after bit-shift, the operation is deemed safe, thus no warning from both compiler.
推荐阅读
- phonegap-plugins - 如何将 Google Assistant 集成到 phonegap 应用程序?
- react-native - 无法从“node_modules\react-native-svg\lib\extract\extractProps.js”解析“./patternReg”
- python - 在 Flask 中初始化全局数据连接的并发安全方法
- swift - 如何以未来的方式更新使用 NSDatePicker 设置的日期并保持时间?
- xamarin.forms - 应用程序在同一个 UI 线程中启动之前的等待方法
- java - 如何使用没有objectId的spring-data mongodb进行更新?
- html - 无法在 onclick 函数中传递多个参数
- amazon-web-services - 在没有凭证的情况下验证 AmazonS3 客户端
- java - 是否可以在 java 7 中创建流和过滤器
- java - 如何将 Int 数组转换为单个 long 值