这个封装的内部在分配了之后对于返回值做了检查。
malloc.h
#ifndef __sd_malloc_h
#define __sd_malloc_h
#include <stddef.h>
#include <stdlib.h>
#include "defs.h"
/**
* @file malloc.h
*/
__SD_BEGIN_DECLS
typedef void (*sd_malloc_handler_t)();
extern sd_malloc_handler_t sd_malloc_set_handler(void (*a_handler)());
#ifndef __SD_DEBUG__
extern void *sd_malloc(size_t n);
extern void *sd_calloc(size_t n, size_t s);
extern void *sd_realloc(void *p, size_t n);
extern char *sd_strdup (const char *__str);
#else
#define sd_malloc malloc
#define sd_calloc calloc
#define sd_realloc realloc
#define sd_strdup strdup
#endif
__SD_END_DECLS
#endif
头文件中一样包含着一对__SD_BEGIN_DECLS和__SD_END_DECLS,以及头文件defs.h的包含。
使用typedef定义了一个函数类型:typedef void (*sd_malloc_handler_t)(); 标识 sd_malloc_handler_t代表一个函数类型,
用这个类型可以定义类似如下结构的函数变量:
void test()
然后是根据一个宏:__SD_DEBUG__作为编译开关进行选择两套内存申请函数,一个是用宏进行定义的接口,实际就是系统本身的内存操作接口。另一个是自行实现的一组函数:sd_malloc、sd_calloc、sd_realloc、sd_strdup。
malloc.c里面就是对于在debug模式下的时候所实现的头文件中声明的这一组函数。
malloc.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <stdlib.h>
#include "error.h"
#if defined(__APPLE__)
# include <sys/time.h>
# include <crt_externs.h>
# define environ (*_NSGetEnviron())
#endif /* __APPLE__ */
typedef void (*sd_malloc_handler_t)();
static sd_malloc_handler_t handler = NULL;
static void *
fixup_null_alloc (n)
size_t n;
{
void* p = 0;
#ifdef HAVE_SBRK
static char* first_break = NULL;
if (n == 0)
p = malloc ((size_t) 1);
if (p == 0) {
extern char **environ;
size_t allocated;
if (first_break != NULL)
allocated = (char *) sbrk (0) - first_break;
else
allocated = (char *) sbrk (0) - (char *) &environ;
sd_error("\nCannot allocate %lu bytes after allocating %lu bytes\n",
(unsigned long) n, (unsigned long) allocated);
if (handler)
handler();
else {
sd_error("\n\tMemory exhausted !! Aborting.\n");
abort();
}
}
#endif
return p;
}
sd_malloc_handler_t
sd_malloc_set_handler(a_handler)
sd_malloc_handler_t a_handler;
{
sd_malloc_handler_t previous = handler;
handler = a_handler;
return previous;
}
/* Allocate N bytes of memory dynamically, with error checking. */
void *
sd_malloc (n)
size_t n;
{
void *p;
p = malloc (n);
if (p == 0)
p = fixup_null_alloc (n);
return p;
}
/* Allocate memory for N elements of S bytes, with error checking. */
void *
sd_calloc (n, s)
size_t n, s;
{
void *p;
p = calloc (n, s);
if (p == 0)
p = fixup_null_alloc (n * s);
return p;
}
/* Change the size of an allocated block of memory P to N bytes,
with error checking.
If P is NULL, run sd_malloc. */
void *
sd_realloc (p, n)
void *p;
size_t n;
{
if (p == 0)
return sd_malloc (n);
p = realloc (p, n);
if (p == 0)
p = fixup_null_alloc (n);
return p;
}
/* Return a newly allocated copy of STRING. */
char *
sd_strdup (string)
const char *string;
{
return strcpy (sd_malloc (strlen (string) + 1), string);
}
其实每个函数的实现里面,都是先调用系统函数进行内存分配,当内存分配不成功的时候会调用fixup_null_alloc函数进行再次尝试。熟悉了ansi C的同学看着这里的函数的定义会觉得比较怪异,其实Ansi C之前的函数形式就是这样的(K&R C风格),Ansi C为了兼容性也实现支持这种格式。参数还需要再这里重新定义下;
然后对于 fixup_null_alloc的实现着重的研究下,因为下面所有函数的实现在失败的时候都会去调用这个函数,然后另外还有一个函数就是sd_malloc_set_handler,我在log4c里面搜索了一下,这个函数没有被调用的地方,也没有文字性的说明,搞不懂到底是要做什么,所以暂时不进行分析。
fixup_null_alloc函数的定义在上面的第二十五行到第五十六行。其中第二十八行到三十五行在传入参数为0的时候做了一次判断并根据判断进行调用系统函数进行内存的分配。接下来的三十六行到五十三行是利用sbrk系统调用获取而这里参数给的是0,可以利用这个调用获取当前program break的位置。然后进行计算,打印一下当前系统中内存的情况进行报错处理。
至于为什么用sbrk (0) - first_break;或者用sbrk (0) - (char *) &environ;
首先使用sbrk (0)能够知道当前系统中可用堆的结束的位置,然后static关键字声明的first_break在bss段的最后,两者相减就能得到一共有多少内存已经分配出去了。至于environ为什么,还没有理解。
至于使用方式上就拿这一组当成系统的内存操作接口就可以了。示例程序就不啰嗦了。