Does C have a generic “pointer to a pointer” type?

◇◆丶佛笑我妖孽 提交于 2019-12-10 10:16:43

问题


For example, if I wanted to write a "free" that nulled the pointer, I could write something like:

void myfree(void **data) {
    free(*data);
    *data = NULL;
}

however, when I try to write this, I get a compiler warning (from gcc 4.6.2) saying: warning: passing argument 1 of ‘myfree’ from incompatible pointer type [enabled by default] ... note: expected ‘void **’ but argument is of type ‘char **‘ (in this case, I am freeing a char array).

It seems that void* is special cased to avoid this kind of warning, since calloc, free etc. don't trigger such warnings, but void** is not (given the above). Is the only solution an explicit cast, or have I misunderstood something?

[I am revisiting some pain points in a recent project, wondering how they could have been handled better, and so am poking at corner cases, hence the C questions today.]

update given that void* is special cased, I could hack around this using void* and casts inside myfree, but that would be a somewhat irresponsible solution because everyone and their dog are going to pass a pointer to something that looks like free, so I need some kind of compiler warning based on "degree of indirection" for this to be a practical solution. hence the idea of a generic "pointer to a pointer".


回答1:


Technically, the standard allows different object pointer types to have different representations (even different sizes), although char* and void* are required have the same representation. But the following is UB:

int *ip = 0;
free(*(void**)(&ip));

simply because the memory for ip need not be the same size as the memory for a void*, and even if it is the bit pattern for a null pointer of type int* need not be the same as the bit pattern for a null pointer of type void*. If they're different, then of course the compiler has to insert code to convert between them whenever you convert an int* to void* or back.

In practice, implementations don't do that to you (and for example Posix forbids it).

More importantly though, the strict aliasing rules don't allow you to access a char* object using an lvalue of type void*. So in practice, concerns about pointer representation will not break your code, but the optimizer actually might. Basically, if the function call myfree((void**)(&p)) gets inlined, then the compiler might see:

char *p = <something>;
void **data = (void**)(&p);
free(*data);
*data = NULL;
// code that reads p

The optimizer is allowed to note that *data = NULL is setting an object of type void*, whereas the "code that reads p" is reading an object of type char*, which is forbidden from being aliased with that other, void* object over there. So it is allowed to reorder the instructions, eliminate *data = NULL; entirely, or possibly other things I haven't thought of that will ruin your day, but that would speed the code up if you hadn't broken the rules.




回答2:


You can use the MACRO to do this operation. This will be really great compare to having a function; I hope you know the advantage of using MACRO.

#define FREE_IF_NOT_NULL(x) if (x != NULL) { \
                                            free(x); \
                                            x = NULL; \
                                       }


来源:https://stackoverflow.com/questions/10953740/does-c-have-a-generic-pointer-to-a-pointer-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!