我如何强制 C++ 编译器对可能不需要的整数提升发出警告?
How can I force a C++ compiler to warn at possibly unwanted integral promotions?
今天我想写一个小函数来以二进制形式呈现最多 64 位的整数。我 运行 遇到了一个问题,这显然是由于静默整数提升引起的。下面的源代码显示了问题。
#include <iostream>
#include <string>
using namespace std;
string pretty(uint64_t);
string pretty_correct(uint64_t);
string pretty(uint64_t var)
{
string result;
result.reserve(50);
for ( short i=63; i>=0; --i ) {
if ( var & (0x1 << i) )
result.append(1,'|');
else
result.append(1,'O');
}
return result;
}
string pretty_correct(uint64_t var)
{
string result;
result.reserve(50);
for ( short i=63; i>=0; --i ) {
if ( var & (static_cast<unsigned short>(0x1 << i)))
result.append(1,'|');
else
result.append(1,'O');
}
return result;
}
int main() {
cout << pretty(12345678901234567890U) << " <- wrong!"<< endl;
cout << pretty_correct(12345678901234567890U) << " <- right!"<< endl;
}
我正在寻找一种方法让 GCC(或者可能是 Clang)发出针对此类问题的警告。到目前为止呈现给我的所有解决方案都让我以某种方式先见之明地意识到可能会发生这种问题。但是现在发生这种情况的方式可能会在我的代码中引入细微的错误,这将很难追踪。
编辑:这些是我用于编译的命令:
g++ -std=c++14 -Wall -Wextra -pedantic-errors -Wconversion -g -o testcase testcase.cpp
clang -std=c++14 -Weverything -g -o testcase testcase.cpp -lm -lstdc++
首先,您的 pretty_correct()
函数实际上是不正确的。原因是表达式 0x1 << i
在 i >= 32
时产生 UB:您只能按不超过类型长度 (source) 的符号进行移位。这可能是您希望看到的警告。
有几种方法可以修复它。第一个是将 type-qualifier 添加到 1:
if (var & (1ull << i))
虽然我更喜欢
if ((var >> i) & 1)
因为您不必考虑 "must it be 1u or 1ll or whatever"。
现在关于警告:no, there are no such warnings既不在 GCC 中也不在 Clang 中。但是,Clang sanitizer 为您提供了在运行时使用 -fsanitize=shift
:
检查它的选项
$ clang++-3.5 -std=c++11 -Wall -Wextra -O2 a.cpp -fsanitize=shift
$ ./a.out
a.cpp:14:25: runtime error: shift exponent 63 is too large for 32-bit type 'int'
|||O|O||OOO|||||OOOO|O|O||O|OO|O|||O|O||OOO|||||OOOO|O|O||O|OO|O <- wrong!
a.cpp:28:53: runtime error: shift exponent 63 is too large for 32-bit type 'int'
OOOOOOOOOOOOOOOOOOOO|O|O||O|OO|OOOOOOOOOOOOOOOOOOOOO|O|O||O|OO|O <- right!
今天我想写一个小函数来以二进制形式呈现最多 64 位的整数。我 运行 遇到了一个问题,这显然是由于静默整数提升引起的。下面的源代码显示了问题。
#include <iostream>
#include <string>
using namespace std;
string pretty(uint64_t);
string pretty_correct(uint64_t);
string pretty(uint64_t var)
{
string result;
result.reserve(50);
for ( short i=63; i>=0; --i ) {
if ( var & (0x1 << i) )
result.append(1,'|');
else
result.append(1,'O');
}
return result;
}
string pretty_correct(uint64_t var)
{
string result;
result.reserve(50);
for ( short i=63; i>=0; --i ) {
if ( var & (static_cast<unsigned short>(0x1 << i)))
result.append(1,'|');
else
result.append(1,'O');
}
return result;
}
int main() {
cout << pretty(12345678901234567890U) << " <- wrong!"<< endl;
cout << pretty_correct(12345678901234567890U) << " <- right!"<< endl;
}
我正在寻找一种方法让 GCC(或者可能是 Clang)发出针对此类问题的警告。到目前为止呈现给我的所有解决方案都让我以某种方式先见之明地意识到可能会发生这种问题。但是现在发生这种情况的方式可能会在我的代码中引入细微的错误,这将很难追踪。
编辑:这些是我用于编译的命令:
g++ -std=c++14 -Wall -Wextra -pedantic-errors -Wconversion -g -o testcase testcase.cpp
clang -std=c++14 -Weverything -g -o testcase testcase.cpp -lm -lstdc++
首先,您的 pretty_correct()
函数实际上是不正确的。原因是表达式 0x1 << i
在 i >= 32
时产生 UB:您只能按不超过类型长度 (source) 的符号进行移位。这可能是您希望看到的警告。
有几种方法可以修复它。第一个是将 type-qualifier 添加到 1:
if (var & (1ull << i))
虽然我更喜欢
if ((var >> i) & 1)
因为您不必考虑 "must it be 1u or 1ll or whatever"。
现在关于警告:no, there are no such warnings既不在 GCC 中也不在 Clang 中。但是,Clang sanitizer 为您提供了在运行时使用 -fsanitize=shift
:
$ clang++-3.5 -std=c++11 -Wall -Wextra -O2 a.cpp -fsanitize=shift
$ ./a.out
a.cpp:14:25: runtime error: shift exponent 63 is too large for 32-bit type 'int'
|||O|O||OOO|||||OOOO|O|O||O|OO|O|||O|O||OOO|||||OOOO|O|O||O|OO|O <- wrong!
a.cpp:28:53: runtime error: shift exponent 63 is too large for 32-bit type 'int'
OOOOOOOOOOOOOOOOOOOO|O|O||O|OO|OOOOOOOOOOOOOOOOOOOOO|O|O||O|OO|O <- right!