这个问题是关于使用指针算术与struct Offsets得出的指针。
考虑以下程序:
#include <cstddef>
#include <iostream>
#include <new>
struct A {
float a;
double b;
int c;
};
static constexpr auto off_c = offsetof(A, c);
int main() {
A * a = new A{0.0f, 0.0, 5};
char * a_storage = reinterpret_cast<char *>(a);
int * c = reinterpret_cast<int *>(a_storage + off_c));
std::cout << *c << std::endl;
delete a;
}
此程序似乎有效,并使用默认设置和C 11标准对我测试的编译器给出了预期结果。
(一个密切相关的程序,我们使用void *
而不是char *
和static_cast
而不是reinterpret_cast
,不是普遍接受的。gcc 5.4
发出有关用无效指针的指针算术的警告,clang 6.0
说指针使用void *
的算术是一个错误。)
该程序根据C 标准具有明确定义的行为?
答案是否取决于实现是放松还是严格的指针安全([basic.stc.dynamic.safety]
)?
您的代码中没有基本错误。
如果A
不是普通的旧数据,则以上是UB(在C 17之前),并有条件地支持(C 17之后)。
您可能需要用auto*
替换char*
和int*
,但这是一种样式的东西。
请注意,对成员的指针以类型安全的方式进行了完全相同的事情。大多数编译器将指向成员的指针作为类型中成员的偏移。但是,他们确实在任何地方都可以在任何地方工作。
旁边:我看不出offsetof
是标准中的constexpr
。;)
无论如何,替换:
static constexpr auto off_c = offsetof(A, c);
static constexpr auto off_c = &A::c;
和
auto* a_storage = static_cast<char *>(a);
auto* c = reinterpret_cast<int *>(a_storage + off_c));
auto* c = &(a->*off_c);
以C 方式进行。
它在您的特定示例中是安全的,但这仅仅是因为您的struct是标准布局,您可以使用std::is_standard_layout<>
进行仔细检查。
试图将其应用于:
之类的结构struct A {
float a;
double b;
int c;
std::string str;
};
即使字符串已经超过结构的一部分,
也是非法的。
编辑
引起的内容:在3.7.4.3中[BASIC.STC.DYNAMIC.SAFETY]它说,仅当(条件)和我们具有严格的指针安全性时,指针是安全得出的这样的地方。在5.7指针算术中,它说我可以在数组中进行平常的算术,但是我看不到任何东西告诉我struct offsect oftsotiget antmecety是可以的。我试图弄清楚这是否与我认为的方式无关,或者在假设的"严格"中,构造偏移算术是否不正确,或者我读过5.7错误(N4296)
当您执行指针算术时,您将在char
的数组中执行它,至少sizeof(A)
的大小,所以很好。
然后,当您归还第二个成员时,您将被(2.4)覆盖:
- 安全衍生的指针值明确定义的指针转换(4.10,5.4)的结果;
您应该检查您的假设。
假设#1)Offsetof给出了字节中正确的偏移。仅当该类被视为具有许多限制(例如没有虚拟方法)的"标准路线"时,才可以保证这一点。确保。
假设#2)char与字节的大小相同。在C中,根据定义,这是安全的。
假设#3)偏移给出了从指针到类的正确偏移,而不是从数据开头。这基本上与#1相同,但是VTable肯定是一个问题。同样,仅与标准层一起使用。