严格的别名规则 [英] Strict aliasing rule

查看:63
本文介绍了严格的别名规则的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读有关reinterpret_cast及其别名规则的说明( http://en.cppreference.com/w/cpp/language/reinterpret_cast ).

I'm reading notes about reinterpret_cast and it's aliasing rules ( http://en.cppreference.com/w/cpp/language/reinterpret_cast ).

我写了这段代码:

struct A
{
  int t;
};

char *buf = new char[sizeof(A)];

A *ptr = reinterpret_cast<A*>(buf);
ptr->t = 1;

A *ptr2 = reinterpret_cast<A*>(buf);
cout << ptr2->t;

我认为这些规则不适用于这里:

I think these rules doesn't apply here:

  • T2是对象(可能具有cv限定)的动态类型
  • T2和T1都是指向同一类型T3(自C ++ 11起)的指针(可能是多层的,可能在每个级别上都是cv限定的)的指针
  • T2是一个聚合类型或并集类型,将上述类型之一作为元素或非静态成员(包括递归地包含子并集的子聚合元素和所包含并集的非静态数据成员):安全地从结构的第一个成员和联合的元素强制转换为包含该结构的结构/联合.
  • T2是对象的动态类型的(可能是cv限定的)带符号或无符号的变体
  • T2是对象的动态类型的(可能是cv限定的)基类
  • T2是char或unsigned char

我认为此代码不正确.我对吗?代码正确与否吗?

In my opinion this code is incorrect. Am I right? Is code correct or not?

另一方面,连接功能(man 2 connect)和struct sockaddr又如何呢?

On the other hand what about connect function (man 2 connect) and struct sockaddr?

   int connect(int sockfd, const struct sockaddr *addr,
               socklen_t addrlen);

例如.我们有struct sockaddr_in,我们必须将其强制转换为struct sockaddr.上面的规则也不适用,所以这是不正确的吗?

Eg. we have struct sockaddr_in and we have to cast it to struct sockaddr. Above rules also doesn't apply, so is this cast incorrect?

推荐答案

是的,它是无效的,但不是因为您要将 char * 转换为 A * :这是因为您没有获得实际上指向 A * A * ,并且,如您所知,没有任何类型别名选项适合.

Yeah, it's invalid, but not because you're converting a char* to an A*: it's because you are not obtaining a A* that actually points to an A* and, as you've identified, none of the type aliasing options fit.

您将需要以下内容:

#include <new>
#include <iostream>

struct A
{
  int t;
};

char *buf = new char[sizeof(A)];

A* ptr = new (buf) A;
ptr->t = 1;

// Also valid, because points to an actual constructed A!
A *ptr2 = reinterpret_cast<A*>(buf);
std::cout << ptr2->t;

现在根本不使用类型别名(尽管请继续阅读,因为还有更多工作要做!).

Now type aliasing doesn't come into it at all (though keep reading because there's more to do!).

实际上,这还不够.我们还必须考虑 alignment .尽管上面的代码似乎可以正常工作,但是为了完全安全起见,您需要将 new 放置在正确对齐的存储区域中,而不是随意的 char s.

In reality, this is not enough. We must also consider alignment. Though the above code may appear to work, to be fully safe and whatnot you will need to placement-new into a properly-aligned region of storage, rather than just a casual block of chars.

标准库(自C ++ 11起)为我们提供了 std :: aligned_storage 来做到这一点:

The standard library (since C++11) gives us std::aligned_storage to do this:

using Storage = std::aligned_storage<sizeof(A), alignof(A)>::type;
auto* buf = new Storage;

或者,如果您不需要动态分配它,只需:

Or, if you don't need to dynamically allocate it, just:

Storage data;

然后,重新放置您的位置:

Then, do your placement-new:

new (buf) A();
// or: new(&data) A();

并使用它:

auto ptr = reinterpret_cast<A*>(buf);
// or: auto ptr = reinterpret_cast<A*>(&data);

所有内容如下:

#include <iostream>
#include <new>
#include <type_traits>

struct A
{
  int t;
};

int main()
{
    using Storage = std::aligned_storage<sizeof(A), alignof(A)>::type;

    auto* buf = new Storage;
    A* ptr = new(buf) A();

    ptr->t = 1;

    // Also valid, because points to an actual constructed A!
    A* ptr2 = reinterpret_cast<A*>(buf);
    std::cout << ptr2->t;
}

(实时演示)

即使那样,自C ++ 17以来,这还有些复杂.有关更多信息,请参见相关的cpp引用页面,并注意 std:: launder .

当然,这整个过程看起来都是人为的,因为您只需要一个 A ,因此不需要数组形式.实际上,您只需要首先创建沼泽标准 A .但是,假设 buf 实际上实际上更大,并且您正在创建分配器或类似的东西,这是有道理的.

Of course, this whole thing appears contrived because you only want one A and therefore don't need array form; in fact, you'd just create a bog-standard A in the first place. But, assuming buf is actually larger in reality and you're creating an allocator or something similar, this makes some sense.

这篇关于严格的别名规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆