首页 > 解决方案 > 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.

最后,我设法通过强制转换来“修复”1uintmax_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?

标签: cgccvisual-c++warnings

解决方案


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.


推荐阅读