题
我有一个测试结构定义如下:
struct test{
int a, b, c;
bool d, e;
int f;
long g, h;
};
在某处我以这种方式使用它:
test* t = new test; // create the testing struct
int* ptr = (int*) t;
ptr[2] = 15; // directly manipulate the third word
cout << t->c; // look if it really affected the third integer
这在我的Windows上正常工作 - 它按预期打印15,但它是否安全?我是否可以确定变量是在内存中我想要的 - 特别是在这种组合结构的情况下(例如f在我的编译器上是第五个单词,但它是第六个变量)?
如果没有,是否还有其他方法可以直接操作struct成员而不在代码中实际使用struct-&gt;成员构造?
解决方案
看起来你在问两个问题
以3长度int arrray治疗和测试是否安全?
最好避免这种情况。这可能是C ++标准中定义的操作,但即使是这样,与您合作的每个人都不太可能理解您在此处所做的事情。我相信如果你阅读标准是不支持的,因为有可能填充结构,但我不确定。
是否有更好的方式来访问没有它名字的成员?
是。尝试使用 offsetof 宏/运算符。这将提供结构中特定成员的内存偏移量,并允许您将点正确定位到该成员。
size_t offset = offsetof(mystruct,c);
int* pointerToC = (int*)((char*)&someTest + offset);
另一种方法是直接取c的地址
int* pointerToC = &(someTest->c);
其他提示
不,你不能确定。编译器可以在结构成员之间自由引入填充。
要添加到 JaredPar的答案,只能使用C ++中的另一个选项(不是在普通的C)中创建一个指向成员的指针对象:
struct test
{
int a, b, c;
bool d, e;
int f;
long g, h;
};
int main(void)
{
test t1, t2;
int test::*p; // declare p as pointing to an int member of test
p = &test::c; // p now points to 'c', but it's not associating with an object
t1->*p = 3; // sets t1.c to 3
t2->*p = 4; // sets t2.c to 4
p = &test::f;
t1->*p = 5; // sets t1.f to 5
t2->*p = 6; // sets t2.f to 6
}
您可能正在寻找 offsetof
宏。这将获得成员的字节偏移量。然后,您可以在该偏移处操纵该成员。但请注意,此宏是特定于实现的。包括 stddef.h
以使其正常工作。
它可能不安全,100%不可读;从而使这种代码在现实生产代码中变得不可接受。
使用set方法和boost :: bind来创建functor,它将改变这个变量。
除了填充/对齐问题,其他答案已经提出,您的代码违反了严格的别名规则,这意味着它可能会因优化构建而中断(不确定MSVC如何执行此操作,但GCC -O3
将打破这种行为)。本质上,因为 test * t
和 int * ptr
具有不同的类型,编译器可能会假设它们指向内存的不同部分,并且可能会重新排序操作。
考虑这个小修改:
test* t = new test;
int* ptr = (int*) t;
t->c = 13;
ptr[2] = 15;
cout << t->c;
最后的输出可以是 13
或 15
,具体取决于编译器使用的操作顺序。
根据标准的第9.2.17段,将指向结构的指针强制转换为指向其第一个成员的指针实际上是合法的,前提是结构为 POD :
指向POD-struct对象的指针, 用a适当转换 reinterpret_cast,指向它 初始成员(或者如果该成员是 位字段,然后到其中的单位 它驻留),反之亦然。 [注意: 因此可能有未命名的 在POD结构对象中填充, 但必要时不要在它的开头 实现适当的对齐。 ]
但是,标准不保证结构的布局 - 甚至POD结构 - 除了后面的成员的地址将大于先前成员的地址,前提是没有访问说明符(<代码> private:, protected:
或 public:
)。因此,将 struct test
的初始部分视为3个整数的数组是技术上未定义的行为。