我有一个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?
答案 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
。由于积分值在移位之前为负,在移位之后也为负,因此该操作被认为是安全的,因此两个编译器均未发出警告。