【问题标题】:Is it legal to alias a char array through a pointer to int?通过指向 int 的指针对 char 数组进行别名是否合法?
【发布时间】:2016-12-16 04:04:37
【问题描述】:

我知道标准中明确允许以下内容:

int n = 0;
char *ptr = (char *) &n;
cout << *ptr;

这个怎么样?

alignas(int) char storage[sizeof(int)];
int *ptr = (int *) &storage[0];
*ptr = 0;
cout << *ptr;

本质上,我问的是别名规则是否允许通过指向另一种类型的指针访问一系列字符。如果可能的话,我想参考标准中指明一种或另一种方式的部分。

标准的某些部分让我感到矛盾; (3.10.10) 似乎表明假设storage 的动态类型不是int,这将是未定义的行为。然而,动态类型的定义我并不清楚,std::aligned_storage 的存在会让我相信这可能的。

【问题讨论】:

  • 投反对票的人可以离开 cmets 吗?
  • 我不知道您是否可以这样做,但是标准定义了以您尝试的方式访问aligned_storage中的变量
  • 我没有投票,但如果要求参考标准,我希望您展示您自己的研究并引用您认为相关的部分 - 并描述您自己不确定的结论.
  • @chbaker0 查看我在上面给出的链接,示例实现(希望符合标准)与您在这里所做的几乎相同
  • @user2079303 好点,我会在我的研究中添加让我感到矛盾的研究

标签: c++ language-lawyer strict-aliasing


【解决方案1】:

代码int *ptr = (int *) &amp;storage[0]; *ptr = 0; 违反严格的别名规则(C++14 [basic.lval]/10)导致未定义的行为

被访问的对象的类型为char,但用于访问的glvalue的类型为int

char 的“对象的动态类型”仍然是 char。 (dynamic type 仅在派生类的情况下与静态类型不同)。 C++ 也没有任何等效于 C 的“有效类型”,它允许通过在 malloc 空间中使用赋值运算符来“创建”类型化对象。


关于std::aligned_storage的正确使用,你应该使用placement-new在存储中创建一个对象。使用placement-new 被认为结束char(或其他)对象的生命周期,并创建一个指定类型的新对象(动态存储持续时间),重复使用相同的存储。这样就不会有严格的混叠违规。

你可以对 char 数组做同样的事情,例如:

alignas(int) char storage[sizeof(int)];
int *ptr = new(storage) int;
*ptr = 0;
cout << *ptr;

请注意,内置类型 int 不需要伪析构函数调用或 delete。如果使用具有非平凡初始化的类类型,则需要这样做。 Link to further reading

【讨论】:

  • 第二部分进入了我误解的核心;我没有意识到对象类型是通过使用放置new建立的。
  • 所以这意味着使用 malloc 然后将结果内存用作 int 或除 char 之外的任何其他原语也是未定义的,除非您执行新放置?
  • @chbaker0 是的,它不是很知名;忽略placement-new 并执行严格的混叠违规似乎在大多数(或所有)时间都有效,因此很多人只是在没有意识到问题或不关心标准的情况下这样做。
  • 根据标准 - 这是正确的,您必须在写入 malloc 空间之前使用placement-new。
  • @curiousguy 该标准不支持您的主张。真的,说声明一个 char 数组实际上会产生无限数量的对象是荒谬的。该主题是关于标准的实际内容,而不是您希望它说的内容。
【解决方案2】:

union 构造在这里可能很有用。

union 类似于struct,只是union 的所有元素都占用相同 的存储区域。

换句话说,它们是“查看同一事物的不同方式”,就像 FORTRAN 的 EQUIVALENCE 声明一样。因此,例如:

union {
  int   foo;
  float bar;
  char  bletch[8];
}

提供三种完全不同的方式来考虑相同的存储区域。 (union 的 storage-size 是其最长组件的大小。)foobarbletch 都是 same storage 的同义词。

union 经常与 typedef 一起使用,如 StackOverflow 文章中所述:C: typedef union

【讨论】:

  • 该问题带有 C++ 标签,根据stackoverflow.com/questions/25664848/unions-and-type-punning,这在 C++ 中是不合法的(但它在 C 中)。
  • 这个stackoverflow.com/questions/28521188/… 说了同样的话。
  • C++ 不允许通过 unions 进行类型双关。您只能读出最近分配给的成员。
  • 哦,富...你是多么正确...我不好。 :*( ... ("type punning" ...不知怎的我以前没听过这个词。但是,我喜欢它。)
  • @M.M "您只能读出最近分配给的成员" 这也不是真的。
【解决方案3】:
*ptr = 0;

写入一个 int,所以它是对 int 的访问,具有 int 类型的左值,所以这部分代码是可以的。

强制转换在道德上是好的,但 C/C++ 标准文本没有清楚地描述强制转换、指针或任何基本内容。

【讨论】:

  • 他们确实清楚地描述了它,你似乎不同意标准所说的含义,所以你假装它没有这么说。
  • @M.M "他们确实描述得很清楚" 我很高兴知道这一点。请查看并回答我关于指针的许多问题:stackoverflow.com/q/32100245/963864
  • @M.M 这里暗示任何对 union 的使用直到最近才被定义是非常愚蠢的。
猜你喜欢
  • 2017-09-03
  • 2016-12-02
  • 2021-07-28
  • 1970-01-01
  • 1970-01-01
  • 2018-04-05
  • 2021-01-26
  • 1970-01-01
相关资源
最近更新 更多