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);
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).