下面的代码通过一些位攻击执行快速反平方根操作.该算法可能是由Silicon Graphics在1990年代早期开发的,它也出现在Quake 3中. 更多信息
但是我从GCC C++编译器收到以下警告:解除引用类型惩罚指针将破坏严格别名规则
我应该使用static_cast,reinterpret_cast还是dynamic_cast在这种情况下使用?
float InverseSquareRoot(float x)
{
float xhalf = 0.5f*x;
int32_t i = *(int32_t*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}
Run Code Online (Sandbox Code Playgroud) c++ strict-aliasing gcc-warning undefined-behavior type-punning
我试图从浮点数中提取位而不调用未定义的行为.这是我的第一次尝试:
unsigned foo(float x)
{
unsigned* u = (unsigned*)&x;
return *u;
}
Run Code Online (Sandbox Code Playgroud)
据我了解,由于严格的别名规则,这不能保证工作,对吧?如果使用字符指针进行中间步骤,它是否有效?
unsigned bar(float x)
{
char* c = (char*)&x;
unsigned* u = (unsigned*)c;
return *u;
}
Run Code Online (Sandbox Code Playgroud)
或者我是否必须自己提取单个字节?
unsigned baz(float x)
{
unsigned char* c = (unsigned char*)&x;
return c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
}
Run Code Online (Sandbox Code Playgroud)
当然,这有一个缺点,取决于字节顺序,但我可以忍受.
工会黑客肯定是未定义的行为,对吧?
unsigned uni(float x)
{
union { float f; unsigned u; };
f = x;
return u;
}
Run Code Online (Sandbox Code Playgroud)
为了完整起见,这里有一个参考版本foo.也是未定义的行为,对吗?
unsigned ref(float x)
{ …Run Code Online (Sandbox Code Playgroud) 将双数组转换为由双精度构成的结构是否可以?
struct A
{
double x;
double y;
double z;
};
int main (int argc , char ** argv)
{
double arr[3] = {1.0,2.0,3.0};
A* a = static_cast<A*>(static_cast<void*>(arr));
std::cout << a->x << " " << a->y << " " << a->z << "\n";
}
Run Code Online (Sandbox Code Playgroud)
这打印1 2 3.但它是否保证每次都可以与任何编译器一起工作?
编辑:根据
9.2.21:指向标准布局结构对象的指针,适当转换?使用reinterpret_cast,指向其初始成员(...),反之亦然.
如果我用我的代码替换
struct A
{
double & x() { return data[0]; }
double & y() { return data[1]; }
double & z() { return data[2]; }
private:
double data[3];
}; …Run Code Online (Sandbox Code Playgroud) 我有一个类模板A,其中包含一个指针容器(T*):
template <typename T>
class A {
public:
// ...
private:
std::vector<T*> data;
};
Run Code Online (Sandbox Code Playgroud)
和一堆功能,如:
void f(const A<const T>&);
void g(const A<const T>&);
Run Code Online (Sandbox Code Playgroud)
是否可以将呼叫通过铸造这些功能从A<const T>到A<T>?
A<double> a;
...
auto& ac = reinterpret_cast<const A<const double>&>(a);
f(ac);
Run Code Online (Sandbox Code Playgroud)
我很确定这段代码有不确定的行为.
在现实生活中使用这种转换是危险的吗?
假设我正在开发一个名为 libModern 的库。该库使用称为 libLegacy 的遗留 C 库作为实现策略。libLegacy 的界面如下所示:
typedef uint32_t LegacyFlags;
struct LegacyFoo {
uint32_t x;
uint32_t y;
LegacyFlags flags;
// more data
};
struct LegacyBar {
LegacyFoo foo;
float a;
// more data
};
void legacy_input(LegacyBar const* s); // Does something with s
void legacy_output(LegacyBar* s); // Stores data in s
Run Code Online (Sandbox Code Playgroud)
出于各种原因,libModern 不应该向用户公开 libLegacy 的类型,其中包括:
处理这种情况的教科书方法是 pimpl 习惯用法:libModern 将提供一个包装类型,该类型内部有一个指向遗留数据的指针。然而,这在这里是不可能的,因为 libModern 无法分配动态内存。一般来说,其目标不是增加大量开销。
因此,libModern 定义了自己的类型,这些类型与遗留类型布局兼容,但具有更好的接口。在此示例中,它使用强标志enum而不是普通uint32_t标志:
enum class ModernFlags : …Run Code Online (Sandbox Code Playgroud) 我从网络接收缓冲区,该缓冲区被转换为32位字的数组.我有一个单词被我的界面文档定义为ieee float.我需要从缓冲区中提取这个单词.在不调用转换的情况下从一种类型转换为另一种类型很困难.这些位已经符合ieee浮点标准,我不想重新排列任何位.
我的第一次尝试是将地址转换uint32_t为a void*,然后将其转换void*为a float*,然后取消引用为float:
float ieee_float(uint32_t f)
{
return *((float*)((void*)(&f)));
}
Run Code Online (Sandbox Code Playgroud)
错误:解除引用类型惩罚指针将破坏严格别名规则[-Werror = strict-aliasing]
我的第二次尝试是这样的:
float ieee_float(uint32_t f)
{
union int_float{
uint32_t i;
float f;
} tofloat;
tofloat.i = f;
return tofloat.f;
}
Run Code Online (Sandbox Code Playgroud)
然而,街上的一句话是,工会完全不安全.从最近没有编写的联合成员读取它是未定义的行为.
所以我尝试了更多的C++方法:
float ieee_float(uint32_t f)
{
return *reinterpret_cast<float*>(&f);
}
Run Code Online (Sandbox Code Playgroud)
错误:解除引用类型惩罚指针将破坏严格别名规则[-Werror = strict-aliasing]
我的下一个想法是"搞砸它.为什么我要处理指针呢?" 并尝试过:
float ieee_float(uint32_t f)
{
return reinterpret_cast<float>(f);
}
Run Code Online (Sandbox Code Playgroud)
错误:从'uint32_t {aka unsigned int}'类型无效转换为'float'类型
有没有办法在不触发警告/错误的情况下进行转换?我用g ++编译-Wall -Werror.我宁愿不接触编译器设置.
我标记了C,因为c解决方案是可以接受的.
std::bit_cast显然是在 c++20 中引入的。并std::start_lifetime_as建议用于 c++23(来自P0593R5)。由于它们似乎都要求所涉及的数据类型无论如何都是微不足道的,一旦引入后者,是否还需要前者?
对于没有提供有关这些新功能的更多信息,提前致歉。我只是在观看了关于类型双关的 cppcon 2019 讲座后才听说过它们,而且我无法start_lifetime_as通过谷歌找到太多相关信息。我希望看到这个的其他人可能会知道更多。
uint32_t Seed() {
uint64_t seed = GetSomeReasonable64BitIntegerSeed();
return *(uint32_t*)&seed ^ *((uint32_t*)&seed + 1);
}
Run Code Online (Sandbox Code Playgroud)
上面不是真正的代码,但这基本上是真正的代码所做的.我从g ++那里得到一个警告,它违反了严格的别名,谷歌搜索它,好吧我想解决它.我发现了这个问题,但它没有提供一个明确的解决方案,除了使用memcpy或依赖于未定义但实际上没有问题的行为,即访问联合的未设置成员.
我能想到的当前选择是,
memcpy.union并将此部分编译为C,其中语言标准允许通过联合进行类型惩罚.I'm worried if my implementation is unsafe or could be improved. Suppose I have an array of bytes (more specifically, std::byte); is casting an element's address to a void pointer and then to any datatype safe?
template <typename To>
constexpr To *byte_cast(std::byte &From) {
return static_cast<To *>(static_cast<void *>(&From));
}
// or rather
template <typename To>
constexpr To *byte_cast(std::byte *From) {
return static_cast<To *>(static_cast<void *>(From));
}
Run Code Online (Sandbox Code Playgroud)
I'm avoiding reinterpret_cast due to its unreliability. Do note that I want to the …
c++ ×9
type-punning ×5
c++20 ×2
arrays ×1
bits ×1
c++17 ×1
casting ×1
gcc ×1
gcc-warning ×1
static-cast ×1
struct ×1
templates ×1