It is common to assign pointers with allocations using an implicit function-return void * conversion, just like malloc()'s:
void *malloc(size_t size);
int *pi = malloc(sizeof *pi);
I would like to perform the same assignment while passing the address of the target pointer, and without explicitly casting its type from within the function (not within its body, nor arguments).
The following code seems to achieve just that.
.
#include <stdio.h>
#include <stdlib.h>
int allocate_memory(void *p, size_t s) {
void *pv;
if ( ( pv = malloc(s) ) == NULL ) {
fprintf(stderr, "Error: malloc();");
return -1;
}
printf("pv: %p;\n", pv);
*((void **) p) = pv;
return 0;
}
int main(void) {
int *pi = NULL;
allocate_memory(&pi, sizeof *pi);
printf("pi: %p;\n", (void *) pi);
return 0;
}
Result:
pv: 0x800103a8;
pi: 0x800103a8;
a void* is a pointer, but the type of what it points to is unspecified. When you pass a void pointer to a function you will need to know what its type was in order to cast it back to that correct type later in the function to use it.
In C, you don't need to cast the return value of malloc . The pointer to void returned by malloc is automagically converted to the correct type. However, if you want your code to compile with a C++ compiler, a cast is needed.
void pointer in C / C++ A void pointer can hold address of any type and can be typecasted to any type.
The void pointer in C is a pointer that is not associated with any data types. It points to some data location in the storage. This means that it points to the address of variables. It is also called the general purpose pointer.
Types int**
and void**
are not compatible
You are casting p, whose real type is int**, to void** and then dereferencing it here:
*((void **) p) = pv;
which will break aliasing rules.
You can either pass a void pointer and then cast it correctly:
void *pi = NULL;
int* ipi = NULL ;
allocate_memory(&pi, sizeof *ipi );
ipi = pi ;
or return a void pointer.
int *pi = allocate_memory(sizeof *pi);
There is an option to use a union:
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
union Pass
{
void** p ;
int** pi ;
} ;
int allocate_memory(union Pass u , size_t s) {
void *pv;
if ( ( pv = malloc(s) ) == NULL ) {
fprintf(stderr, "Error: malloc();");
return -1;
}
printf("pv: %p;\n", pv);
*(u.p) = pv;
return 0;
}
int main()
{
int* pi = NULL ;
printf("%p\n" , pi ) ;
allocate_memory( ( union Pass ){ .pi = &pi } , sizeof( *pi ) ) ;
printf("%p\n" , pi ) ;
return 0;
}
As far as I understand it, this example conforms to standard.
Use static asserts to guarantee that the sizes and alignment are the same.
_Static_assert( sizeof( int** ) == sizeof( void** ) , "warning" ) ;
_Static_assert( _Alignof( int** ) == _Alignof( void** ) , "warning" ) ;
No, this is not compliant. You're passing an int**
as void*
(ok), but then you cast the void*
to a void**
which is not guaranteed to have the same size and layout. You can only dereference a void*
(except one gotten from malloc
/calloc
) after you cast it back to the pointer type that it originally was, and this rule does not apply recursively (so a void**
does not convert automatically, like a void*
).
I also don't see a way to meet all your requirements. If you must pass a pointer by pointer, then you need to actually pass the address of a void*
and do all the necessary casting in the caller, in this case main
. That would be
int *pi;
void *pv;
allocate_memory(&pv, sizeof(int));
pi = pv;
... defeating your scheme.
I don't think it's possible to do it in a 100% standard-compliant manner, because non-void pointers are not guaranteed to have the strictly same size as a void*
.
It's the same reason the standard demands explicitly casting printf("%p")
arguments to void*
.
Added: On the other hand, some implementations mandate that this work, such as Windows (which happily casts IUnknown**
to void**
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With