【发布时间】:2021-08-18 07:08:08
【问题描述】:
违反严格别名规则会产生未定义的行为,例如当通过网络将结构发送到 char 缓冲区时,然后该 char 指针是 C-style/std::reinterpret_cast() 强制转换为结构指针。
C++ std::bit_cast() function 看起来可以用于以(实现?)定义的方式转换此类指针,即不违反严格别名规则。
例子:
#include <sys/types.h>
#include <netinet/in.h>
#include <bit>
int get_sock_addr(const struct sockaddr *a)
{
struct sockaddr_in *x = std::bit_cast<struct sockaddr_in*>(a);
return x->sin_addr.s_addr;
}
所以get_sock_addr() 的调用者不知何故获得了一个sockaddr 指针,并确定它实际上指向了一个sockaddr_in 结构。
那么,这种通过std::bit_cast() 进行的指针转换是一个有效的用例吗?
或者它是否也会以某种方式产生未定义的行为?
如果是定义的行为,标准是否将这种指针转换归类为实现定义的行为?
如果没有值表示对应于 To 的对象表示,则返回值未指定。
那么,在不同的指针表示不兼容以致不能相互对应的情况下,是否可以使用符合标准的编译器?
【问题讨论】:
-
您的示例及其假设是
reinterpret_cast的标准有效用例。 -
@DavisHerring 嗯,这真的取决于在调用示例 get_sock_addr 函数之前/之后对 sockaddr 指针做了什么,对吧?例如,对于
sockaddr *a = getfromsomewhere(); if (a->sa_family == AF_INET) addr = get_sock_addr(a); ... } ...之类的东西,sockaddr_in 对象是通过严格别名规则未涵盖的类型的 2 个别名指针访问的,对吗? -
在问题中你只是说“它实际上指向一个
sockaddr_in结构”。您刚刚提供的代码试图验证该假设,但实际上与它不兼容(不幸的是,因为这是传统 C 接口的设计方式)。 common-initial-subsequence 规则旨在允许这种标记,但它们需要一个实际的联合。 -
@DavisHerring 很好,在问题中我说调用者'已经确定它实际上指向一个 sockaddr_in 结构'。因此,我在上一条评论中给出的代码是此确定步骤的一种可能实现方式。当然,可以说,在原始示例代码中包含该步骤会成为一个更好的示例。
-
我们达成了激烈的协议。该代码是确定隐藏在指针后面的实际类型的明显方法——但 C++ 不允许您这样做,因为它涉及使用对象作为不同的类型来进行确定。
标签: c++ language-lawyer c++20 strict-aliasing