首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >c++11严格别名规则是否允许通过char *、char(&)[N]、甚至std::array<char、N>&访问uint64_t?

c++11严格别名规则是否允许通过char *、char(&)[N]、甚至std::array<char、N>&访问uint64_t?
EN

Stack Overflow用户
提问于 2017-12-21 09:26:07
回答 2查看 335关注 0票数 5

根据堆栈溢出的答案,关于C++11/14严格的别名规则:

如果程序试图通过除下列类型之一以外的glvalue访问对象的存储值,则行为是未定义的:

  • 对象的动态类型,
  • 对象的动态类型的cv限定版本,
  • 类似于对象的动态类型的类型(如4.4中所定义的),
  • 对象的动态类型对应的有符号或无符号类型的类型,
  • 对象的动态类型的cv限定版本对应的有符号或无符号类型的类型,
  • 在其元素或非静态数据成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含的联合的元素或非静态数据成员),
  • 对象的动态类型的(可能是cv限定的)基类类型的类型,
  • charunsigned char类型。

我们是否可以使用

(1) char *

(2) char(&)[N]

(3) std::array<char, N> &

而不依赖于未定义的行为

代码语言:javascript
运行
复制
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调用都产生:

代码语言:javascript
运行
复制
endian
6

e : 65
n : 6e
d : 64
i : 69
a : 61
n : 6e

/g/cdDTAM (启用-f-混叠-混叠=2)

wandbox.org/permlink/pGvPCzNJURGfEki7 7

EN

回答 2

Stack Overflow用户

发布于 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示例 ]

票数 3
EN

Stack Overflow用户

发布于 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 charstd::byte数组。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47921786

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档