Q假设我想编写一个函数,它接受一个通用指针作为参数,并且我想模拟按引用传递。我能否将形式参数类型声明为void **,并执行类似这样的操作?
void f(void **); double *dp; f((void **)&dp);
A不能移植。类似这样的代码可能会生效,有时也被推荐,但它依赖于所有指针类型的内部表示都相同(这很常见,但并非普遍;请参见问题 5.17)。
C 语言中没有通用的指向指针的指针类型。void *充当通用指针,仅仅是因为当其他指针类型被赋值给它或者从中赋值时,会自动应用(必要的)转换;当试图对一个指向指针类型(非void *)的void **值进行解引用时,无法执行这些转换。void *当您使用一个void **指针值(例如,当您使用*运算符访问void *指向的void **值)时,编译器无法知道该void *值是否曾从其他指针类型转换而来。它必须假定它仅仅是一个void *;它不能执行任何隐式转换。
换句话说,您操作的任何void **值都必须是某个实际void *值的地址;像(void **)&dp这样的类型转换,虽然可能让编译器不报错,但不可移植(甚至可能无法达到您想要的效果;另请参见问题 13.9)。如果void **指向的指针不是void *,并且它的大小或表示方式与void *不同,那么编译器将无法正确访问它。
为了让上面那段代码片段生效,您需要使用一个中间void *变量
double *dp; void *vp = dp; f(&vp); dp = vp;对vp的赋值和从中赋值,让编译器有机会执行必要的转换。
同样,到目前为止的讨论都假定不同的指针类型可能有不同的尺寸或表示方式,这在今天很少见,但并非闻所未闻。为了更清楚地理解void **的问题,请将这种情况与涉及到诸如类型int和double之类的类似情况进行比较,它们很可能具有不同的尺寸,并且肯定具有不同的表示方式。如果我们有一个函数
void incme(double *p) { *p += 1; }那么我们可以这样做:
int i = 1; double d = i; incme(&d); i = d;和i将被增加 1。(这与涉及辅助void **的正确vp代码类似。)另一方面,如果我们尝试做类似
int i = 1; incme((double *)&i); /* WRONG */(这段代码与问题中的片段类似)这样的事情,那极不可能生效。