Is it legal to modify any data pointer through a void **

自闭症网瘾萝莉.ら 提交于 2019-12-23 09:26:00

问题


Is it legal to access a pointer type through a void **?

I've looked over the standards quotes on pointer aliasing but I'm still unsure on whether this is legal C or not:

int *array;
void **vp = (void**)&array;
*vp = malloc(sizeof(int)*10);

Trivial example, but it applies to a more complex situation I'm seeing.

It seems that it wouldn't be legal since I'm accessing an int * through a variable whose type is not int * or char *. I can't come to a simple conclusion on this.

Related:

  • Does C have a generic "pointer to a pointer" type?
  • C-FAQ question 4.9

回答1:


Pointer to different types can have different sizes.

You can store a pointer to any type into a void * and then you can recover it back but this means simply that a void * must be large enough to hold all other pointers.

Treating a variable that is holding an int * like it's indeed a void * is instead, in general, not permitted.

Note also that doing a cast (e.g. casting to int * the result of malloc) is something completely different from treating an area of memory containing an int * like it's containing a void *. In the first case the compiler is informed of the conversion if needed, in the second instead you're providing false information to the compiler.

On X86 however they're normally the same size and you're safe if you just play with pointers to data (pointers to functions could be different, though).

About aliasing any write operation done through a void * or a char * can mutate any object so the compiler must consider aliasing as possible. Here however in your example you're writing through a void ** (a different thing) and the compiler is free to ignore potentially aliasing effects to int *.




回答2:


No. void ** has a specific type (pointer to a pointer-to-void). I.e. the underlying type of the pointer is "pointer-to-void"

You're not storing a like-pointer value when storing a pointer-to-int. That a cast is required is a strong indicator what you're doing is not defined behavior by the standard (and it isn't). Interestingly enough, however, you can use a regular void* coming and going and it will exhibit defined behavior. In other words, this:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *array;
    void *vp = &array;
    int **parray = vp;
    *parray = malloc(sizeof(int)*10);
}

is legitimate. Your original example won't even compile if I remove the cast and use apple llvm 4.2 (clang), due precisely to incompatible pointer types, i.e. the very subject of your question. The specific error is:

"Incompatible pointer types initializing 'void **' with an expression of type 'int **'"

and rightfully so.




回答3:


Your code may work on some platforms, but it is not portable. The reason is that C doesn't have a generic pointer to pointer type. In the case of void * the standard explicitly permits conversions between it and other pointer to complete/incomplete types, but this is not the case with void **. What this means is that in your code, the compiler has no way of knowing if the value of *vp was converted from any type other than void *, and therefore can not perform any conversions except the one you explicitly cast yourself.

Consider this code:

void dont_do_this(struct a_t **a, struct b_t **b)
{
    void **x = (void **) a;
    *x = *b;
}

The compiler will not complain about the implicit cast from b_t * to void * in the *x = *b line, even though that line is trying to put a pointer to a b_t in a place where only pointers to a_t should be put. The mistake is in fact in the previous line, which is converting "a pointer to a place where pointers to a_t can be put" to "a pointer to a place where pointers to anything can be put". This is the reason there is no implicit cast possible. For an analogous example with pointers to arithmetic types, see the C FAQ.

Your cast, then, even though it shuts the compiler warning up, is dangerous because not all pointer types may have the same internal representation/size (e.g. void ** and int *). To make your code work in all cases, you have to use an intermediate void *:

int *array;
void *varray = array;
void **vp = &varray;
*vp = malloc(sizeof(int) * 10);


来源:https://stackoverflow.com/questions/18551785/is-it-legal-to-modify-any-data-pointer-through-a-void

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