C: Why aren't &(void *) and void** compatible?
Asked Answered
P

2

7

I'm receiving an incompatible pointer type error warning although my code functions properly. First of all, here's a simplified version of my code:

typedef struct {
    int field1;
    int field2;
} my_struct;

void some_function(void **data){
    // do something with *data
}

int main(int argc, char *argv[]){
    my_struct *ptr = malloc(sizeof(my_struct));
    ptr->field1 = 5;
    ptr->field2 = 17;
    some_function(&ptr);
}

The my_struct type is one example, but I'm actually using multiple types, so some_func must accept void**, rather than my_struct**. Each time I call some_func in the manner above, I recieve the following warning, and yet my code functions properly.

warning: passing argument 1 of ‘my_func’ from incompatible pointer type

I'm unsure of why these types aren't compatible, and would like to remove the warning in some way. For answers, feel free to explain why this is happening and, if possible, how to remove the warning (cpp directive, maybe?). Thanks!

Parole answered 24/12, 2013 at 0:24 Comment(3)
Note that &(void *) isn't a thing. & is just an operator, not part of a type.Saint
@Oli: Though, in C, the meaning is pretty unambiguous here :) (In C++ of course it could mean something else)Thurible
By &(void *), I meant the result of &(some_pointer). Thanks for the quick replies - I've just gotten started with C.Parole
T
5

void* means (effectively) "a pointer to an untyped region of memory".

void** means (effectively) "a pointer to a region of memory, which contains pointers to untyped regions of memory".

Notice how the block in the middle has a type -- an array of void*. You're passing in effectively an array of mystruct*, which is a different type. So your compiler warns you.

Similar issues occur in C++ when trying to convert Derived** to Base**; but it applies here in C as well. (void* operates like the "base class" of all pointer types) Consider a piece of code like this:

void does_things(void** outParameter)
{
    *outParameter = malloc(42); // Looks fine; writing void* to void* :)
}

int main()
{
    my_struct* x = /*...*/;
    does_things(&myStruct); // Ut oh, tries to make x point to
                            // something that is *not* a my_struct
}
Thurible answered 24/12, 2013 at 0:28 Comment(9)
"void* means (effectively) "a pointer to an untyped region of memory"." - not really; it means "a pointer to a region of memory whose type is not specified by the pointer". An object referenced by a void pointer can still have a declared or effective type.Sweated
@davmac: That's what I said "effectively" :) Didn't think going into particulars here would help the answer.Thurible
A region of memory (or "object") doesn't become ("effectively" or otherwise) untyped just because a void * points at it. Mis-use of "effectively" perhaps; I felt it was worth clarifying. What (I think) you meant was that a void pointer does not denote the type of the object (or memory region) it points to, which is certainly correct.Sweated
@davmac: A region of memory doesn't have a type at all -- the type it has comes strictly via the mechanism used to access it. If a piece of code has a void pointer, then as far as that piece of code is concerned the memory region to which the void pointer refers is untyped.Thurible
not true; please check the spec. 6.5 paragraph 6 (in both C99 and C11). A void pointer denotes no type, but that doesn't mean the object pointed to has no type.Sweated
@davmac: The C standard describes an abstract machine, which is very different than the way real machines behave. To real machines, memory is just a bunch of bits. If we want the C standard technical description of what's going on we have to bring up storage duration and what it means for something to be an object, etc.; but none of that helps answer the question and as such aren't appropriate for this answer describing high-level conceptual ideas. If you don't like it, fine, downvote the answer; but I'm not making changes over this.Thurible
I suppose a less abstract example is if you take the address of an int variable and cast it to a void *. Is the region of memory that the pointer points to untyped? No, it is clearly of type int. Compilers can be aware of this and do unexpected things if you assume otherwise ("undefined behavior"). I'm not going to downvote your answer; my comments stand, as do your responses. Let's leave it at that; readers can make up their own minds, and it's good that the discussion is public. Anyone interested in more should do web searches for "type punning" and "undefined behavior".Sweated
@davmac: No, clearly the memory has no type. Memory does not have a type. If you want to speak about things in terms of the C abstract machine, there is no such thing as "memory" -- only objects which have a given storage duration. But as I said before, none of that has any relevance to the question.Thurible
When you write in C you must follow the rules of the "C abstract machine" as you call it; if you don't, you're in the realm of undefined behavior. Compilers rely on those rules to perform optimisations and breaking those rules can result in code that doesn't do what you think it should. No such thing as "memory"? - well, the standard defines objects in terms of "data storage" which is clearly the same thing. Relevance to the question? Perhaps limited, but that's no excuse for spreading fallacies.Sweated
E
11

Use a void * parameter type.

void ** is not a generic pointer type. void * is.

Electrophoresis answered 24/12, 2013 at 0:26 Comment(4)
As a side question: does void ** even mean anything? I.e. is it ever useful?Agricola
@Agricola Why wouldn't it mean anything? It's a pointer-to-pointer-to-void. It's useful when... wait for it... you need a pointer-to-pointer-to-void.Ruvolo
Ah, Billy ONeal's answer goes more into that. I should probably go to bed.Agricola
@Kninnug: A typical scenario is when you need to return a void* but are already using the return value of a function for something else -- you end up using an out parameter, which has type void**. (See my answer for an example)Thurible
T
5

void* means (effectively) "a pointer to an untyped region of memory".

void** means (effectively) "a pointer to a region of memory, which contains pointers to untyped regions of memory".

Notice how the block in the middle has a type -- an array of void*. You're passing in effectively an array of mystruct*, which is a different type. So your compiler warns you.

Similar issues occur in C++ when trying to convert Derived** to Base**; but it applies here in C as well. (void* operates like the "base class" of all pointer types) Consider a piece of code like this:

void does_things(void** outParameter)
{
    *outParameter = malloc(42); // Looks fine; writing void* to void* :)
}

int main()
{
    my_struct* x = /*...*/;
    does_things(&myStruct); // Ut oh, tries to make x point to
                            // something that is *not* a my_struct
}
Thurible answered 24/12, 2013 at 0:28 Comment(9)
"void* means (effectively) "a pointer to an untyped region of memory"." - not really; it means "a pointer to a region of memory whose type is not specified by the pointer". An object referenced by a void pointer can still have a declared or effective type.Sweated
@davmac: That's what I said "effectively" :) Didn't think going into particulars here would help the answer.Thurible
A region of memory (or "object") doesn't become ("effectively" or otherwise) untyped just because a void * points at it. Mis-use of "effectively" perhaps; I felt it was worth clarifying. What (I think) you meant was that a void pointer does not denote the type of the object (or memory region) it points to, which is certainly correct.Sweated
@davmac: A region of memory doesn't have a type at all -- the type it has comes strictly via the mechanism used to access it. If a piece of code has a void pointer, then as far as that piece of code is concerned the memory region to which the void pointer refers is untyped.Thurible
not true; please check the spec. 6.5 paragraph 6 (in both C99 and C11). A void pointer denotes no type, but that doesn't mean the object pointed to has no type.Sweated
@davmac: The C standard describes an abstract machine, which is very different than the way real machines behave. To real machines, memory is just a bunch of bits. If we want the C standard technical description of what's going on we have to bring up storage duration and what it means for something to be an object, etc.; but none of that helps answer the question and as such aren't appropriate for this answer describing high-level conceptual ideas. If you don't like it, fine, downvote the answer; but I'm not making changes over this.Thurible
I suppose a less abstract example is if you take the address of an int variable and cast it to a void *. Is the region of memory that the pointer points to untyped? No, it is clearly of type int. Compilers can be aware of this and do unexpected things if you assume otherwise ("undefined behavior"). I'm not going to downvote your answer; my comments stand, as do your responses. Let's leave it at that; readers can make up their own minds, and it's good that the discussion is public. Anyone interested in more should do web searches for "type punning" and "undefined behavior".Sweated
@davmac: No, clearly the memory has no type. Memory does not have a type. If you want to speak about things in terms of the C abstract machine, there is no such thing as "memory" -- only objects which have a given storage duration. But as I said before, none of that has any relevance to the question.Thurible
When you write in C you must follow the rules of the "C abstract machine" as you call it; if you don't, you're in the realm of undefined behavior. Compilers rely on those rules to perform optimisations and breaking those rules can result in code that doesn't do what you think it should. No such thing as "memory"? - well, the standard defines objects in terms of "data storage" which is clearly the same thing. Relevance to the question? Perhaps limited, but that's no excuse for spreading fallacies.Sweated

© 2022 - 2024 — McMap. All rights reserved.