所以我最近开始了一场运动来清除一些警告,当我在GCC中启用所有编译器警告(-Wall -Wextra -Wpedantic)时,我得到了一个关于未初始化值的相当混乱的警告。下面是对正在发生的事情的一个(稍微简化的)视图:
#include <cstdint>
#include <iostream>
constexpr uint64_t MASK = 0xFFFFFFFFFFFFFFFF;
double do_something(const double& rhs) {
double tmp(rhs);
reinterpret_cast<uint64_t&>(tmp) &= MASK;
return tmp;
}
int main() {
std::cout << do_something(3.14159);
}
编制
g++ -std=c++17 -O3 -Wall -Wextra -Wpedantic
基本上,我对双精度体的值应用位掩码MASK
,然后对原始数据和掩码数据执行一些其他操作。另一个操作涉及到这里不相关的额外状态,位掩码存储在其他地方,但正确定义。奇怪的是,我得到了以下编译器警告
<source>: In function 'double do_something(const double&)':
<source>:8:32: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
8 | reinterpret_cast<uint64_t&>(tmp) &= MASK;
| ^~~
<source>:8:37: warning: 'tmp' is used uninitialized [-Wuninitialized]
8 | reinterpret_cast<uint64_t&>(tmp) &= MASK;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
<source>:7:11: note: 'tmp' declared here
7 | double tmp(rhs);
,尽管警告的上下文指向对重新解释的数据的&=操作。我认为这是因为编译器无法通过重新解释转换看到在(重新解释)对uint64_t的引用中在上面的行上初始化的有效数据。这是正确的吗?如果没有,有谁能开导我一下吗?
免责声明:我知道你们中的一些人可能会对重新解释的强制转换感到反感,但这对这段代码的运行方式至关重要,而且(不考虑警告)它是"安全的"。
使用memcpy
。不要键入双关语指针。
#include <cstring>
#include <cstdint>
#include <iostream>
constexpr uint64_t MASK = 0xFFFFFFFFFFFFFFFF;
double do_something2(const double& rhs) {
double tmp(rhs);
uint64_t tmp64 = 0;
static_assert(sizeof(uint64_t) >= sizeof(double));
memcpy(&tmp64, &tmp, sizeof(double));
tmp64 &= MASK;
memcpy(&tmp, &tmp64, sizeof(double));
return tmp;
}
int main() {
std::cout << do_something2(3.14159);
}
你也可以使用unsigned char
来检查任何东西:
double do_something3(const double& rhs) {
double tmp(rhs);
unsigned char *tmpu = reinterpret_cast<unsigned char*>(&tmp);
uint64_t mask = MASK; // TODO: handle endianess
for(int i = 0; i < sizeof(double) / CHAR_BIT; ++i) {
const unsigned char tmpmask = mask;
mask >>= CHAR_BIT;
*tmpu &= tmpmask;
}
return tmp;
}