views:

108

answers:

3

Assuming this program:

#include <stdio.h>
#include <string.h>

static void ring_pool_alloc(void **p, size_t n) {
    static unsigned char pool[256], i = 0;
    *p = &pool[i];
    i += n;
}

int main(void) {
    char *str;
    ring_pool_alloc(&str, 7);
    strcpy(str, "foobar");
    printf("%s\n", str);
    return 0;   
}

... is it possible to somehow avoid the GCC warning

test.c:12: warning: passing argument 1 of ‘ring_pool_alloc’ from incompatible pointer type
test.c:4: note: expected ‘void **’ but argument is of type ‘char **’

... without casting to (void**) (or simply disabling the compatibility checks)? Because I would very much like to keep compatibility warnings regarding indirection-level...

+1  A: 

Simply change:

static void ring_pool_alloc(void **p, size_t n) {

to:

static void ring_pool_alloc(char **p, size_t n) {
anon
This is would be better, but I think he wants to pass things other then `char **`.
Artefacto
@Artefacto: Can't be done, because it's not possible to change a variable without knowing its actual type. That's why returning a `void *` is OK - because the original variable is changed at the function call site, where its type is known.
caf
+1  A: 

Change ring_pool_alloc to receive a void *. You can then recast to void ** in the function, if you wish.

Or in your specific case:

/* You can pass any pointer as a first parameter */
static void ring_pool_alloc(void *r, size_t n) {
    unsigned char **p = r; /* no cast necessary */
    static unsigned char pool[256], i = 0;
    *p = &pool[i];
    i += n;
}

Note that void ** cannot act as a generic pointer-to-pointer type. On the other hand, convertions from and to void * with other pointer types are applied automatically.

Artefacto
So casting a `(char **)` to a `(void **)` is somehow undesirable but casting a `(char *)` to a `(char **)` is OK? All to defeat a perfectly acceptable compiler warning?
sizzzzlerz
@sizzzzlerz: The difference is that the cast is *inside* the method and yes, that is perfectly acceptable. But to be honest, I’d be wary of accepting a single pointer when a double pointer is actually passed.
Konrad Rudolph
@sizzzzlerz Just answering the question :p But where is the conversion from `(char *)` to `(char **)`? Anyway, the point is that the compiler generates no warning if you pass any pointer to a function that accepts a `void *`.
Artefacto
@sizzzzlerz And like Konrad Rudolph said, it has the advantage you don't have to cast the argument everytime you call the function.,
Artefacto
"Note that void ** cannot act as a generic pointer-to-pointer type. On the other hand, convertions from and to void * with other pointer types are applied automatically." - I take that as "it's just not possible"?I suppose I'd be fine with a reference from C++... but there I'd have templates too... sigh.
fnawothnig
Oh and... I'd rather cast everywhere I call than _lie_ in the function prototype...
fnawothnig
@fnawothnig You're not lying in the function prototype. A `char **` is a pointer and declaring a function to receive a `void *` means you are accepting a pointer – any pointer, regardless of indirection level. There's just no way to express that you need a pointer to a pointer of any non-pointer type.
Artefacto
+2  A: 

Why don’t you change the method signature such that it returns the new pointer instead of passing it by pointer? In fact, just like regular malloc does:

static void * ring_pool_alloc(size_t n) {
    static unsigned char pool[256], i = 0;
    void *p = &pool[i];
    i += n;
    return p;
}

int main(void) {
    char *str = ring_pool_alloc(7);
    strcpy(str, "foobar");
    printf("%s\n", str);
    return 0;   
}
Konrad Rudolph
Because I need that return value for something else. Sheesh.
fnawothnig
@fnawothnig: that nice `void` return value? Sheesh. If you want correct answers, ask exact questions. And don’t react annoyed when missing information leads to inapplicable answers.
Konrad Rudolph