GCC警告不强制转换,而MSVC警告不强制转换,为什么?

时间:2018-12-04 11:36:41

标签: c gcc visual-c++ warnings

我有一个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。

  

哇,该整数值隐式地从正(移位前)转换为负(移位后)!似乎是一个错误,最好警告他。 -海湾合作委员会的想法

我通过添加(rInt)来强制表达我的意图,使其变成(rInt)((rInt)1 <<...。海湾合作委员会不再抱怨。但是,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?

1 个答案:

答案 0 :(得分:1)

所以我再次去阅读C4310 warning的官方文档,并找到了关键字: cast truncate

显然,MSVC认为有意强制转换无意截断。为何如此?我们来看官方示例。

long int a;
a = (char) 128;   // C4310, use value 0-127 to resolve

对于MSVC,char是默认签名的。由于signed char的范围是-128到127,因此当编译器看到有人试图将128强制转换为char时,肯定会得到完全不同的值-128,因此编译器将发出警告C4310,因为强制转换似乎是一个错误。

但是,如果删除了强制类型转换,而是直接将值分配给char

char c = 128; // warning?

您可能会认为,由于MSVC会巧妙地警告程序员有关可疑的强制转换,因此可以肯定的是,它会对此发出更大的警告。不幸的是,事实并非如此。我再说一次,它不会发出任何警告

因此,如果您输入有误,请使用只能由signed表示的正整数值初始化unsigned变量,MSVC不会警告您。仅当您使用即使数据类型的unsigned版本也无法处理的值进行初始化时,MSVC才会警告您。

char c = 255; // no warning
char c = 256; // warning: truncation from 'int' to 'char'

或者,如果您真的希望MSVC为您进行检查,则需要进行投射。

char c = (char)128; // MSVC emits C4310

因此,我发现C4310没有帮助,甚至可能有害。似乎鼓励C程序员不要强制转换。因为如果您施放,它会发出警告。如果您不投放,则不会发出警告。那么,为什么不铸就可以逃脱呢?这太可笑了,恕我直言。

在海湾合作委员会,情况相反。投?没有警告。没有演员?发出警告。就这么简单。

这样,如果您试图编写在两个编译器下都没有警告地进行编译的类似代码,则您将陷入困境,因为该问题的标题指出, GCC警告不使用强制转换,而MSVC警告不使用强制转换。欢迎来到与其他地方都不兼容的编译器。


赏金

在没有uintmax_t的情况下,在没有来自两个编译器的警告的情况下,找到可以由任意带符号整数类型保留的最小值。

typedef short rInt;
rInt const rIntMin = (rInt)-1 << (sizeof(rInt) * CHAR_BIT - 1);

它如何工作?以16位短为例,代码将1111 1111 1111 1111变为1000 0000 0000 0000。由于积分值在移位之前为负,在移位之后也为负,因此该操作被认为是安全的,因此两个编译器均未发出警告。