访问 POD 结构数组作为其单个成员的数组是否违反严格别名?
Does accessing array of POD struct as array of its single member violate strict aliasing?
我有整数值用于访问不相关数据存储中的数据,即句柄。我选择将整数包装在一个结构中,以便拥有强类型对象,这样不同的整数就不会混淆。它们是而且必须是 POD。这是我正在使用的:
struct Mesh {
int handle;
};
struct Texture {
int handle;
};
我有这些句柄的数组,例如:Texture* textureHandles;
.
有时我需要将句柄数组作为 int*
传递给代码的更通用部分。现在我正在使用:
int* handles = &textureHandles->handle;
本质上采用指向结构的第一个元素的指针并将其解释为数组。
我的问题基本上是这是否合法,或者它是否违反严格的别名操作 int* handles
和 Texture* textureHandles
指向同一内存。我认为这应该被允许,因为在这两种情况下,底层类型 (int
) 的访问方式相同。我的保留与我通过获取一个结构内成员的地址来访问多个结构这一事实有关。
作为我第一个问题的延伸,下面的问题可以吗?
int* handles = reinterpret_cast<int*>(textureHandles);
不,这不能保证有效。特别是,允许编译器在结构的任何元素之后插入填充,但不允许在数组元素之间插入填充。
就是说,对于只有一个元素的结构(int
类型,或者至少有同样大的元素,例如 long
),很可能大多数编译器不会插入任何填充,因此作为一般规则,您当前的使用可能相当安全。
reinterpret_cast<int*>(textureHandles)
绝对和 &textureHandles->handle
一样好。标准中有一个特殊的例外,甚至从 C 继承而来,它表示指向标准布局结构的指针,经过适当转换,指向该结构的初始成员,反之亦然。
用那个修改句柄也可以。它不违反别名规则,因为您正在使用 int
类型的左值来修改 int
.
类型的子对象
递增结果指针,并使用它来访问 Texture
对象数组中的其他元素,虽然有点不确定。 Jerry Coffin 已经指出 sizeof(Texture) > sizeof(int)
是可能的。但是,即使 sizeof(Texture) == sizeof(int)
,指针算法也只是为指向数组的指针定义的(其中任意对象可以被视为长度为 1 的数组)。您在任何地方都没有 int
的数组,因此加法只是未定义。
肯定违反了严格的别名,如果函数可以访问
通过 int*
和 Mesh*
或 Texture*
的数组,你可以
很好地 运行 解决了问题(尽管可能只有当它修改了
以某种方式排列)。
从你对问题的描述来看,我觉得不符合规则
严格的别名才是你真正关心的。真正的问题
是编译器是否可以向不是的结构添加填充
存在于 int
,因此 sizeof( Mesh ) > sizeof( int )
。和
虽然正式的答案是肯定的,但我无法想象一个编译器会
这样做,至少在今天,至少在 int
或更大的类型中
struct
。 (一个词寻址的机器可能会添加填充到
struct
其中仅包含 char
。)
真正的问题可能更多的是通用代码是否是
遗产,不能改变,或者不能。否则,显而易见的解决方案
是创建一个通用句柄类型:
struct Handle
{
int handle;
};
然后从中派生你的类型,或者使用 reinterpret_cast
按照你的建议。有(或至少曾经)保证允许
通过指向不同对象的指针访问 struct
的成员
struct,只要成员和前面的所有成员都相同。
这就是你在 C 中模拟继承的方式。即使保证
已被删除——这是它出现在 C++ 中的唯一原因
是出于 C 兼容性的原因——没有编译器敢违反
考虑到依赖它的现有软件的数量。 (这
例如 Python 的实现。几乎所有 Python
插件,包括用 C++ 编写的插件。)
我有整数值用于访问不相关数据存储中的数据,即句柄。我选择将整数包装在一个结构中,以便拥有强类型对象,这样不同的整数就不会混淆。它们是而且必须是 POD。这是我正在使用的:
struct Mesh {
int handle;
};
struct Texture {
int handle;
};
我有这些句柄的数组,例如:Texture* textureHandles;
.
有时我需要将句柄数组作为 int*
传递给代码的更通用部分。现在我正在使用:
int* handles = &textureHandles->handle;
本质上采用指向结构的第一个元素的指针并将其解释为数组。
我的问题基本上是这是否合法,或者它是否违反严格的别名操作 int* handles
和 Texture* textureHandles
指向同一内存。我认为这应该被允许,因为在这两种情况下,底层类型 (int
) 的访问方式相同。我的保留与我通过获取一个结构内成员的地址来访问多个结构这一事实有关。
作为我第一个问题的延伸,下面的问题可以吗?
int* handles = reinterpret_cast<int*>(textureHandles);
不,这不能保证有效。特别是,允许编译器在结构的任何元素之后插入填充,但不允许在数组元素之间插入填充。
就是说,对于只有一个元素的结构(int
类型,或者至少有同样大的元素,例如 long
),很可能大多数编译器不会插入任何填充,因此作为一般规则,您当前的使用可能相当安全。
reinterpret_cast<int*>(textureHandles)
绝对和 &textureHandles->handle
一样好。标准中有一个特殊的例外,甚至从 C 继承而来,它表示指向标准布局结构的指针,经过适当转换,指向该结构的初始成员,反之亦然。
用那个修改句柄也可以。它不违反别名规则,因为您正在使用 int
类型的左值来修改 int
.
递增结果指针,并使用它来访问 Texture
对象数组中的其他元素,虽然有点不确定。 Jerry Coffin 已经指出 sizeof(Texture) > sizeof(int)
是可能的。但是,即使 sizeof(Texture) == sizeof(int)
,指针算法也只是为指向数组的指针定义的(其中任意对象可以被视为长度为 1 的数组)。您在任何地方都没有 int
的数组,因此加法只是未定义。
肯定违反了严格的别名,如果函数可以访问
通过 int*
和 Mesh*
或 Texture*
的数组,你可以
很好地 运行 解决了问题(尽管可能只有当它修改了
以某种方式排列)。
从你对问题的描述来看,我觉得不符合规则
严格的别名才是你真正关心的。真正的问题
是编译器是否可以向不是的结构添加填充
存在于 int
,因此 sizeof( Mesh ) > sizeof( int )
。和
虽然正式的答案是肯定的,但我无法想象一个编译器会
这样做,至少在今天,至少在 int
或更大的类型中
struct
。 (一个词寻址的机器可能会添加填充到
struct
其中仅包含 char
。)
真正的问题可能更多的是通用代码是否是 遗产,不能改变,或者不能。否则,显而易见的解决方案 是创建一个通用句柄类型:
struct Handle
{
int handle;
};
然后从中派生你的类型,或者使用 reinterpret_cast
按照你的建议。有(或至少曾经)保证允许
通过指向不同对象的指针访问 struct
的成员
struct,只要成员和前面的所有成员都相同。
这就是你在 C 中模拟继承的方式。即使保证
已被删除——这是它出现在 C++ 中的唯一原因
是出于 C 兼容性的原因——没有编译器敢违反
考虑到依赖它的现有软件的数量。 (这
例如 Python 的实现。几乎所有 Python
插件,包括用 C++ 编写的插件。)