假设在我的代码中我必须将void *存储为数据成员,并在需要时将其强制转换回原始类指针.为了测试它的可靠性,我写了一个测试程序(
linux ubuntu 4.4.1 g -04-Wall),我很震惊地看到了这个行为.
struct A { int i; static int c; A () : i(c++) { cout<<"A() : i("<<i<<")\n"; } }; int A::c; int main () { void *p = new A[3]; // good behavior for A* p = new A[3]; cout<<"p->i = "<<((A*)p)->i<<endl; ((A*&)p)++; cout<<"p->i = "<<((A*)p)->i<<endl; ((A*&)p)++; cout<<"p->i = "<<((A*)p)->i<<endl; }
这只是一个测试程序;实际上,对于我的情况,必须将任何指针存储为void *,然后将其强制转换回实际指针(借助模板).所以我们不要担心这一部分. output of the above code是,
p->i = 0 p->i = 0 // ?? why not 1 p->i = 1
但是如果你改变了void * p;到A * p;它给了expected behavior.为什么?
另一个问题,我无法逃避(A *&),否则我不能使用操作符;但它也发出了警告,dereferencing type-punned pointer will break strict-aliasing rules.有没有可行的方法来克服警告?
解决方法
好吧,正如编译器警告你的那样,你违反了严格的别名规则,这正式意味着结果是未定义的.
您可以通过使用增量的函数模板来消除严格的别名冲突:
template<typename T> void advance_pointer_as(void*& p,int n = 1) { T* p_a(static_cast<T*>(p)); p_a += n; p = p_a; }
使用此函数模板,main()的以下定义会在Ideone编译器上生成预期结果(并且不发出警告):
int main() { void* p = new A[3]; std::cout << "p->i = " << static_cast<A*>(p)->i << std::endl; advance_pointer_as<A>(p); std::cout << "p->i = " << static_cast<A*>(p)->i << std::endl; advance_pointer_as<A>(p); std::cout << "p->i = " << static_cast<A*>(p)->i << std::endl; }