C语言–操作内存的工具
C语言本身作为一个操作内存的工具,在数据使用方面都需要直接申请、释放内存,在编码过程中往往不可避免出现一些内存使用的错误。下面一些常用的编码技巧,能帮组尽量减速或排查错误。
一、内存分配
static addr_record cr[256] = {0};
static addr_record fr[256] = {0};
// 4 2 2 4 4
// ptr + size + free + red_zone + data + red_zone
void* app_malloc(long size)
{
void *data = NULL;
if (size <= 0) {
APP_ERROR("size %l error", size);
return data;
}
char *ptr = (char *)malloc(size + 4 + 2 + 2 + 4 + 4);
if (NULL == ptr) {
APP_ERROR("malloc size %l failed", size);
return data;
}
data = ptr + 12;
long address = (long)data;
*((long *) ptr) = address;
*((short*) (ptr+4)) = size;
*((short*) (ptr+6)) = 0;
*((long*) (ptr+8)) = 0x6e6e6e6e;
*((long*) ((char*)data + size)) = 0x6e6e6e6e;
memset(data, 0x0, size);
if (size < 256) {
cr[size].addr = address;
cr[size].size = size;
cr[size].num ++;
}
return data;
}
申请一块内存,在内存前后添加上自拟的字段(也可以理解成协议),例子中最前4个字节是申请内存的首地址,再后2个字节是申请内存大小,再后2字节填0,再后4字节填自定义数,方便查找,申请内存的最后也附上自定义数(自定义数的作用可以方便查找内存开始的地方,也方便做内存间隔断的标识)。
void app_free(void *data) {
if (NULL == data) {
return;
}
char *ptr = (char*) data - 12;
if (*((long*) (ptr+8)) != 0x6e6e6e6e) {
APP_ERROR("Memory out of bounds");
}
short size = *((short*) (ptr+4));
if (size < 0) {
APP_WARN("some one do not use app malloc ?");
} else {
if (size < 256) {
fr[size].size = size;
fr[size].num ++;
}
}
free(ptr);
return;
}
释放内存时也需要向前移动地址,把之前自定义部分包含进去,若报错则可能出现内存问题。
typedef struct {
long addr;
int size;
int num;
} addr_record;
结构体主要用来计数,计算malloc和free次数,方便查内存溢出等问题;
例子
现象:Segmentation fault.
排查过程:gdb运行模式下排查:首先bt查看代码错误位置
确定代码错误发生在app_trigger函数,f 0进入栈查看frame是否正常
确认frame正常则查看entry地址(app_trigger函数参数)
发现entry是个错误地址,再看g_app_ptr缓存中的4个链表指针,发现第二、第三链表的指针地址都存在问题;打印g_app_ptr缓存的内容
根据红框中的数值发现内存并未被越界操作,所以确定是代码本身操作g_app_ptr缓存不当。其中红框中的值是分配g_app_ptr时在其周围设置的防越界标志。
再看代码逻辑,发现app_trigger 调用时可能出现g_app_ptr缓存未初始化或者entry还未注册添加到g_app_ptr缓存中,所以确认是此处可能造成调用顺序不当导致内存错误。
二、内存连续性
内存malloc次数过多会导致内存块连续性降低,内存数据查找速率下降;在结构体内部存在指针,为了避免内存不连续,在给结构体内分配内存时,同时给结构体内部的指针分配对应大小的内存。
typedef struct my_type_s {
struct list_head list;
int repeate;
int up_period;/*更新时间*/
int priority; /*优先级*/
char *info; /*背景详细信息,复杂引导信息*/
} my_type_t;
以上结构体包含一个char指针,一般在使用该结构体的时候先malloc(sizeof(my_type_t))内存,然后再进行my_type_t->info的内存分配并初始化;但建议在分配my_type_t结构体内存的同时分配info的内存,编码如下:
my_type_t* my_malloc(int priv_size) {
my_type_t *my_type = NULL;
my_type = (my_type_t *)app_malloc(sizeof (my_type_t) + priv_size);
if (NULL == my_type) {
ERROR("malloc event failed");
return my_type;
}
my_type->info = (char *)my_type + sizeof (my_type_t);
return event;
}
void event_free(my_type_t *my_type) {
app_free(my_type);
return;
}