gez*_*eza 52 c++ strict-aliasing undefined-behavior language-lawyer c++17
我一直在std::memcpy
用来规避严格的混叠很长一段时间.
例如,检查a float
,像这样:
float f = ...;
uint32_t i;
static_assert(sizeof(f)==sizeof(i));
std::memcpy(&i, &f, sizeof(i));
// use i to extract f's sign, exponent & significand
Run Code Online (Sandbox Code Playgroud)
但是,这次,我检查了标准,我还没有找到任何可以验证这一点的东西.我所发现的是这个:
对于平凡可复制类型T的任何对象(可能重叠的子对象除外),无论对象是否保持类型T的有效值,组成对象的基础字节([intro.memory])都可以复制到char,unsigned char或std :: byte([cstddef.syn])数组.40如果将该数组的内容复制回对象,则该对象应随后保持其原始值.[例如:
Run Code Online (Sandbox Code Playgroud)#define N sizeof(T) char buf[N]; T obj; // obj initialized to its original value std::memcpy(buf, &obj, N); // between these two calls to std?::?memcpy, obj might be modified std::memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type holds its original value
- 结束例子]
与此:
对于任何简单的可复制类型T,如果指向T的两个指针指向不同的T对象obj1和obj2,其中obj1和obj2都不是潜在重叠的子对象,如果构成obj1的基础字节([intro.memory])被复制到OBJ2,41 OBJ2随后应保持相同的值OBJ1.[例如:
Run Code Online (Sandbox Code Playgroud)T* t1p; T* t2p; // provided that t2p points to an initialized object ... std::memcpy(t1p, t2p, sizeof(T)); // at this point, every subobject of trivially copyable type in *t1p contains // the same value as the corresponding subobject in *t2p
- 结束例子]
因此,允许std::memcpy
使用float
to/from char[]
,并且允许std::memcpy
在相同的普通类型之间进行.
我的第一个例子(和链接的答案)是否定义明确?或者检查a的正确方法float
是将std::memcpy
其放入unsigned char[]
缓冲区,并使用shift
s和or
s从中构建一个uint32_t
?
注意:查看std::memcpy
保证可能无法回答这个问题.据我所知,我可以std::memcpy
用简单的字节复制循环替换,问题也是一样的.
小智 22
标准可能无法正确说明这是允许的,但几乎可以肯定,并且据我所知,所有实现都会将此视为已定义的行为.
为了便于复制到实际char[N]
对象中,组成f
对象的字节可以像访问它一样被访问char[N]
.我认为,这一部分没有争议.
char[N]
表示uint32_t
值的字节可以复制到uint32_t
对象中.我认为,这一部分也没有争议.
我相信,同样无可争议的是,例如,fwrite
可能已经在程序的一次运行中写入了字节,并且fread
可能已经在另一次运行中读取它们,或者甚至完全在另一个程序中读取它们.
由于最后一部分,我认为字节来自何处并不重要,只要它们形成某个uint32_t
对象的有效表示即可.您可以循环使用所有float
值,使用memcmp
每个值,直到获得所需的表示,然后您知道它将与uint32_t
您将其解释为的值相同.你可能甚至已经做了在另一个程序,编译器从来没有见过的程序.这本来是有效的.
如果从实现的角度来看,您的代码与明确有效的代码无法区分,那么您的代码必须被视为有效.
eer*_*ika 18
我的第一个例子(和链接的答案)是否定义明确?
行为未定义(除非目标类型具有不由源类型共享的陷阱表示†),但整数的结果值是实现定义的.标准不保证如何表示浮点数,因此无法以可移植的方式从整数中提取尾数等 - 也就是说,使用系统将自己限制在IEEE 754并不会限制你这么多天.
便携性问题:
您可以std::numeric_limits::is_iec559
用来验证您对表示的假设是否正确.
†虽然看起来uint32_t
没有陷阱(见评论)所以你不必担心.通过使用uint32_t
,您已经排除了对深奥系统的可移植性 - 标准的符合系统不需要定义该别名.
Som*_*ken 14
您的示例定义明确,不会破坏严格的别名.std::memcpy
明确指出:
count
将src指向的对象中的字节复制到dest指向的对象.这两个对象都被重新解释为数组unsigned char
.
该标准允许通过a (signed/unsigned) char*
或任何类型别名std::byte
,因此您的示例不显示UB.如果得到的整数具有任何值,则是另一个问题.
use i to extract f's sign, exponent & significand
然而,标准并不保证这一点,因为a的值float
是实现定义的(在IEEE 754的情况下,它将起作用).
归档时间: |
|
查看次数: |
2898 次 |
最近记录: |