根据这堆栈溢出的答案,关于C++11/14严格的别名规则:
如果程序试图通过除下列类型之一以外的glvalue访问对象的存储值,则行为是未定义的:
char
或unsigned char
类型。我们是否可以使用
(1) char *
(2) char(&)[N]
(3) std::array<char, N> &
而不依赖于未定义的行为
constexpr uint64_t lil_endian = 0x65'6e'64'69'61'6e;
// a.k.a. Clockwise-Rotated Endian which allocates like
// char[8] = { n,a,i,d,n,e,\0,\0 }
const auto& arr = // std::array<char,8> &
reinterpret_cast<const std::array<char,8> &> (lil_endian);
const auto& carr = // char(&)[8]>
reinterpret_cast<const char(&)[8]> (lil_endian);
const auto* p = // char *
reinterpret_cast<const char *>(std::addressof(lil_endian));
int main()
{
const auto str1 = std::string(arr.crbegin()+2, arr.crend() );
const auto str2 = std::string(std::crbegin(carr)+2, std::crend(carr) );
const auto sv3r = std::string_view(p, 8);
const auto str3 = std::string(sv3r.crbegin()+2, sv3r.crend() );
auto lam = [](const auto& str) {
std::cout << str << '\n'
<< str.size() << '\n' << '\n' << std::hex;
for (const auto ch : str) {
std::cout << ch << " : " << static_cast<uint32_t>(ch) << '\n';
}
std::cout << '\n' << '\n' << std::dec;
};
lam(str1);
lam(str2);
lam(str3);
}
所有lambda调用都产生:
endian
6
e : 65
n : 6e
d : 64
i : 69
a : 61
n : 6e
/g/cdDTAM (启用-f-混叠-混叠=2)
发布于 2017-12-21 10:21:15
char(&)[N]
案例和std::array<char, N>
案例都会导致未定义的行为。原因已经被你引用过了。注意,char(&)[N]
和std::array<char, N>
都不是与char
相同的类型。
我不确定char
的情况,因为当前的标准并没有明确规定可以将对象看作是一个窄字符数组(进一步讨论请参阅这里 )。
无论如何,如果您想访问对象的底层字节,请使用std::memcpy
,正如标准在[basic.types]/2中显式说明的那样。
对于任何普通可复制类型T的对象(基类子对象除外),无论该对象是否持有T类型的有效值,组成该对象的基础字节(intro.memory)可以复制到一个由字符、无符号字符或
std::byte
(cstddef.syn)组成的数组中。如果将该数组的内容复制回对象中,则该对象随后将保持其原始值。[ 示例: #定义N sizeof of (T) char bufN;T obj;// obj初始化为其原始值std::memcpy(buf & obj,& obj,N);//在这两个对std::memcpy的调用之间,obj可能被修改成std::memcpy(&obj,buf,N);//在这一点上,刀型obj的每个子对象都保持其原始值。 - end示例 ]
发布于 2017-12-21 10:18:45
严格的混叠规则实际上非常简单:如果一个对象不是另一个对象的子对象,则具有重叠生存期的两个对象不能具有重叠存储区域。(*)
然而,它允许读取对象的内存表示形式。对象的内存表示是unsigned char
basic.type/4的序列:
T型对象的对象表示是T型对象所占用的N个
unsigned char
对象的序列,其中N等于sizeof(T)
。对象的值表示是保存T类型值的一组位。
因此,在你的例子中:
lam(str1)
是UB (未定义行为);lam(str2)
是UB (一个数组,其第一个元素不是指针可转换);lam(str3)
并不表示为UB,如果用unsigned char
代替char
,人们可能会认为您正在读取对象表示。(也没有定义它,但它应该适用于所有编译器)因此,使用第三种情况并将p
的声明更改为const unsigned char*
总是会产生预期的结果。对于其他2种情况,它可以使用这个简单的示例,但如果代码更复杂或在更新的编译器版本上,则可能会中断。
(*)该规则有两个例外:一个用于具有公共初始化序列的联合成员;一个用于为其他对象提供存储的unsigned char
或std::byte
数组。
https://stackoverflow.com/questions/47921786
复制相似问题