Reputation: 75
Looking at the source code for e2fsprogs
and wanting to understand the use of internal memory routines. Allocating and freeing.
More to the point why use memcpy
instead of direct handling?
For example ext2fs_get_mem is:
/*
* Allocate memory. The 'ptr' arg must point to a pointer.
*/
_INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
{
void *pp;
pp = malloc(size);
if (!pp)
return EXT2_ET_NO_MEMORY;
memcpy(ptr, &pp, sizeof (pp));
return 0;
}
I guess the use of a local variable is as not to invalidate the passed ptr
in case of malloc
error.
memcpy
instead of setting ptr
to pp
on success?The memory is copied to a local variable, then freed, then memcpy
on the passed pointer to pointer. As the allocation uses memcpy
I guess it has to do some juggling on free as well.
memcpy
do? Isn't sizeof(p)
size of int here?/*
* Free memory. The 'ptr' arg must point to a pointer.
*/
_INLINE_ errcode_t ext2fs_free_mem(void *ptr)
{
void *p;
memcpy(&p, ptr, sizeof(p));
free(p);
p = 0;
memcpy(ptr, &p, sizeof(p));
return 0;
}
ext2_file_t
is defined as:
typedef struct ext2_file *ext2_file_t;
where ext2_file has, amongst other members, char *buf
.
Here we have:
ext2_file_t e2_file;
retval = ext2fs_file_open(current_fs, ino, 0, &e2_file);
It calls ext2fs_file_open()
which do:
ext2_file_t file;
retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
retval = ext2fs_get_array(3, fs->blocksize, &file->buf);
And the free routine is for example:
if (file->buf)
ext2fs_free_mem(&file->buf);
ext2fs_free_mem(&file);
Upvotes: 2
Views: 896
Reputation:
/* The 'ptr' arg must point to a pointer. */
can be read as "The ptr can point to pointer to ANYTHING".
It is a very simple malloc-wrapper in a library; to be useful it has to work for any type. So void *
is the argument.
With a real type the function looks like this, with direct pointer assignment:
int g(unsigned long size, int **ptr)
{
void *pp;
pp = malloc(size);
if (!pp)
return 1;
*ptr = pp;
return 0;
}
The same *ptr = pp
gives a invalid-void error with void *ptr
as argument decalration. Somehow disappointing, but then again it is called void *
, not any *
.
With void **ptr
there is a type warning like:
expected 'void **' but argument is of type 'int **'
So memcpy to the rescue. It looks like even without optimization, the call is replaced by a quadword MOV.
Upvotes: 0
Reputation: 25536
You cannot assign directly to the ptr
parameter, as this is a local variable. memcpy
ing to ptr
actually writes to where the pointer points to. Compare the following usage code:
struct SomeData* data;
//ext2fs_get_mem(256, data); // wrong!!!
ext2fs_get_mem(256, &data);
// ^ (!)
You would achieve exactly the same with a double pointer indirection:
_INLINE_ errcode_t ext2fs_get_mem_demo(unsigned long size, void** ptr)
{
*ptr = malloc(size);
return *ptr ? 0 : EXT2_ET_NO_MEMORY;
}
but this variant requires the pointer being passed to to be of type void*
, which is avoided by the original variant:
void* p;
ext2fs_get_mem_demo(256, &p);
struct SomeData* data = p;
Note: One additional variable and one additional line of code (or at very least one would need a cast)...
Note, too, that in the usage example ext_file_t
should be a typedef
to a pointer type to make this work correctly (or uintptr_t
) or at least have a pointer as its first member (address of struct and address of its first member are guaranteed to be the same in C).
Upvotes: 2