将一个char数组通过指向int的指针进行别名处理是否合法?

11

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

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的存在会使我相信这是可能的。


5
能否给那些点踩的人留下评论吗?请不要改变原意,使翻译更加通俗易懂。 - chbaker0
我不知道你是否能够做到这一点,但按照标准的定义,你正在尝试以一种访问aligned_storage中的变量的方式进行访问。 - DarthRubik
@chbaker0,从我提供的链接来看,它似乎是一个示例实现(希望符合标准),几乎与您在这里所做的事情相同。 - DarthRubik
1
@user2079303 很好的观点,我会在我的研究中加入这个让我感到矛盾的问题。 - chbaker0
@DarthRubik 谢谢,我在我的编辑中注意到了它的存在。 - chbaker0
显示剩余2条评论
3个回答

6

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

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

char 对象的“动态类型”仍然是 char。(动态类型 只在派生类的情况下与静态类型不同)。C++也没有 C 的“有效类型”的等效物,后者允许使用分配运算符将带类型的对象“创建”到 malloc'd 空间中。


关于正确使用std::aligned_storage,您应该使用放置new在存储中创建对象。使用放置new被认为是结束char(或其他类型)对象的生命周期,并创建指定类型的新对象(动态存储期),重用相同的存储。然后就不会有严格别名违规问题。
您可以使用char数组执行相同的操作,例如:
alignas(int) char storage[sizeof(int)];
int *ptr = new(storage) int;
*ptr = 0;
cout << *ptr;

请注意,对于内置类型int,不需要进行伪析构函数调用或delete操作。如果使用具有非平凡初始化的类类型,则需要执行该操作。更多阅读链接

1
这意味着,除非您进行放置 new,否则使用 malloc 分配的内存作为 int 或任何原始类型(而不是 char)也是未定义的? - chbaker0
1
@chbaker0 是的,它并不是很出名;省略放置新对象并违反严格别名规则似乎大多数时间都有效,因此许多人要么不知道这个问题,要么不关心标准,就这样做了。 - M.M
1
根据标准 - 这是正确的做法,您必须在写入之前使用placement-new在malloc空间上。 - M.M
关于“结束生命周期”的部分,请参阅 P0137R1 进行一些调整(以及针对无符号字符数组的特殊情况)。 - T.C.
2
@curiousguy,标准不支持你的说法。实际上,声明一个char数组并不能使得无限数量的对象有效存在,这种说法荒谬可笑。本帖讨论的是标准实际上所说的内容,而不是你希望它所说的内容。 - M.M
显示剩余9条评论

-1

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

union类似于struct,不同之处在于union的所有元素都占据相同的存储区域。

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

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

提供了三种完全不同的方式来考虑相同的存储区域。(联合体的存储大小是其最长成员的大小。)foobarbletch都是指向相同存储的同义词。

union经常与typedef一起使用,正如这篇StackOverflow文章所示:C: typedef union


该问题带有C++标签,根据https://dev59.com/Yl8e5IYBdhLWcg3whqo4,在C++中这是不合法的(但在C中是合法的)。 - Jerry Jeremiah
这个链接https://dev59.com/TV4b5IYBdhLWcg3w7Fgv也是在说同样的事情。 - Jerry Jeremiah
1
C++ 不允许通过联合进行类型转换。您只能从最近分配的成员中读取。 - M.M
1
哦,foo...你说得太对了...我错了。:(...("类型游戏"...不知怎么的,我以前从没听过这个词。但是,我喜欢它。) - Mike Robinson
@M.M "你只能读取最近分配给成员的内容" 这也不是真的。 - curiousguy

-3
*ptr = 0;

写入一个int,因此它是对int的访问,具有int类型的lvalue,因此代码的这部分是正确的。

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


1
他们确实清楚地描述了它,只是你似乎不同意标准所暗示的内容,因此你假装它没有这样说。 - M.M
@M.M "他们确实描述得很清楚" 我很高兴听到这个消息。请查看并回答我关于指针的许多问题:http://stackoverflow.com/q/32100245/963864 - curiousguy
@M.M 这里的暗示是直到最近使用 union 才被定义,这种说法太荒谬了。 - curiousguy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接