要处理的主要问题是对齐。 malloc 返回的指向内存块的指针对于任何对象类型都是正确对齐的,但是对于某些对象类型,您类型的 size 成员末尾的指针可能未正确对齐。您可以通过使用 size 成员和其他一些 max_align_t 类型的对象之间的联合来解决这个问题:
typedef union {
size_t size;
max_align_t reserved__;
} my_data_header;
然后分配一个对象,加上my_data_header 的大小,并使用刚刚超过my_data_header 末尾的指针指向您的对象:
my_data_header *head = malloc(sizeof(*head) + object_size);
if (head) {
head->size = object_size;
object_ptr = (void *)&head[1];
} else {
object_ptr = NULL;
}
然而要释放内存,你需要释放头部,而不是对象指针。或者,可以从对象指针中检索指向头部的指针:
if (object_ptr) {
head = &((my_data_head *)(void *)object_ptr)[-1];
} else {
head = NULL;
}
可以定义一组接口函数以允许分配和释放任何大小的对象并检索当前大小:
obj_alloc.c
#include "obj_alloc.h"
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef union {
size_t size;
max_align_t reserved__;
} obj_hdr;
static obj_hdr *obj_to_hdr(void *obj)
{
if (!obj) {
return NULL;
}
return (obj_hdr *)obj - 1;
}
size_t obj_size(const void *obj)
{
if (!obj) {
return 0;
}
return obj_to_hdr((void *)obj)->size;
}
void *obj_reallocarray(void *obj, size_t nmemb, size_t size)
{
obj_hdr *hdr = obj_to_hdr(obj);
if (!(nmemb && size)) {
/* New size is zero. */
free(hdr);
return NULL;
}
if ((SIZE_MAX - sizeof(*hdr)) / nmemb < size) {
/* Too big */
errno = ENOMEM;
return NULL;
}
hdr = realloc(hdr, sizeof(*hdr) + nmemb * size);
if (!hdr) {
/* Allocation failure. */
errno = ENOMEM;
return NULL;
}
/* Record size and return pointer to object. */
hdr->size = nmemb * size;
return hdr + 1;
}
void *obj_allocarray(size_t nmemb, size_t size)
{
return obj_reallocarray(NULL, nmemb, size);
}
void *obj_calloc(size_t nmemb, size_t size)
{
void *obj = obj_reallocarray(NULL, nmemb, size);
if (obj) {
memset(obj, 0, nmemb * size);
}
return obj;
}
void *obj_malloc(size_t size)
{
return obj_reallocarray(NULL, 1, size);
}
void *obj_realloc(void *obj, size_t size)
{
return obj_reallocarray(obj, 1, size);
}
void obj_free(void *obj)
{
free(obj_to_hdr(obj));
}
obj_alloc.h
#ifndef OBJ_ALLOC_H__INCLUDED
#define OBJ_ALLOC_H__INCLUDED
#include <stddef.h>
/**
* \brief Get current size of object.
*
* Get current size of object allocated by obj_malloc(),
* obj_realloc(), obj_calloc(), obj_allocarray(), or
* obj_reallocarray().
*
* \param[in] obj
* Pointer to object, or null pointer.
*
* \return current size of object.
*/
size_t obj_size(const void *obj);
/**
* \brief Free object.
*
* Free object allocated by obj_malloc(), obj_realloc(),
* obj_calloc(), obj_allocarray(), or obj_reallocarray().
*
* \param[in] obj
* Pointer to object, or null pointer.
*/
void obj_free(void *obj);
/**
* \brief Allocate an object from dynamic memory.
*
* Allocate an object of specified size from dynamic memory and
* record its size.
*
* \param[in] size
* Size of object to be allocated.
*
* \return On success, returns a pointer to the allocated object,
* or a null pointer if \p size is 0. On failure, returns a null
* pointer and sets \c errno to \c ENOMEM.
*/
void *obj_malloc(size_t size);
/**
* \brief Reallocate an object from dynamic memory.
*
* Reallocate an existing object to a new, specified size from
* dynamic memory and record its new size.
*
* obj_realloc(NULL, size) behaves like obj_malloc(size).
*
* obj_realloc(obj, 0) behaves like obj_free(obj) and returns a
* null pointer.
*
* If a pointer to an object is given, it must have been returned
* by a previous call to obj_malloc(), obj_realloc(),
* obj_calloc(), obj_allocarray(), or obj_reallocarray().
*
* \param[in] obj
* Pointer to existing object, or a null pointer.
* \param[in] size
* New size of object to be reallocated.
*
* \return On success, returns a pointer to the reallocated
* object, or a null pointer if \p size is 0. On failure, returns
* a null pointer and sets \c errno to \c ENOMEM, but the original
* object remains unchanged.
*/
void *obj_realloc(void *obj, size_t size);
/**
* \brief Allocate a zeroed array of objects from dynamic memory.
*
* Allocate memory for an array of objects from from dynamic
* memory and with a specified number of elements and a specified
* element size, recording the total size, and setting the memory
* contents to zero.
*
* obj_calloc(nmemb, size) is equivalent to
* obj_malloc(nmemb * size) followed by
* memset(ptr, 0, nmemb * size) if the return value is non-null.
* The call is valid even if the multiplication of \p nmemb by
* \p size would result in arithmetic overflow, but the function
* will fail to allocate memory in that case.
*
* \param[in] nmemb
* Number of elements to allocate.
* \param[in] size
* Size of each element.
*
* \return On success, returns a pointer to the allocated memory,
* or a null pointer if \p size is 0 or \p nmemb is 0. On failure,
* returns a null pointer and sets \c errno to \c ENOMEM.
*/
void *obj_calloc(size_t nmenb, size_t size);
/**
* \brief Allocate an array of objects from dynamic memory.
*
* Allocate memory for an array of objects from from dynamic
* memory and with a specified number of elements and a specified
* element size, recording the total size.
*
* obj_allocarray(nmemb, size) is equivalent to
* obj_malloc(nmemb * size). The call is valid even if the
* multiplication of \p nmemb by \p size would result in
* arithmetic overflow, but the function will fail to allocate
* memory in that case.
*
* \param[in] nmemb
* Number of elements to allocate.
* \param[in] size
* Size of each element.
*
* \return On success, returns a pointer to the allocated memory,
* or a null pointer if \p size is 0 or \p nmemb is 0. On failure,
* returns a null pointer and sets \c errno to \c ENOMEM.
*/
void *obj_allocarray(size_t nmemb, size_t size);
/**
* \brief Reallocate an array of objects from dynamic memory.
*
* Reallocate an existing object to an array of objects from
* dynamic memory with a specified number of elements and a
* specified element size, recording the new total size.
*
* obj_reallocarray(obj, nmemb, size) is equivalent to
* obj_realloc(obj, nmemb * size). The call is valid even if the
* multiplication of \p nmemb by \p size would result in
* arithmetic overflow, but the function will fail to reallocate
* memory in that case.
*
* obj_realloc(obj, 0, size) and obj_realloc(obj, nmemb, 0)
* behave like obj_free(obj) and return a null pointer.
*
* If a pointer to an object is given, it must have been returned
* by a previous call to obj_malloc(), obj_realloc(),
* obj_calloc(), obj_allocarray(), or obj_reallocarray().
*
* \param[in] obj
* Pointer to existing object, or a null pointer.
* \param[in] nmemb
* Number of elements to allocate.
* \param[in] size
* Size of each element.
*
* \return On success, returns a pointer to the reallocated
* memory, or a null pointer if \p nmemb is 0 or \p size is 0.
* On failure, returns a null pointer and sets \c errno to \c
* ENOMEM, but the original object remains unchanged.
*/
void *obj_reallocarray(void *obj, size_t n_memb, size_t size);
#endif