引言
突然感觉要出去走走了, 醒了后 刷完牙就在联系coding, 不知不觉到了 黄昏.
看看天, 打开灯. 又感觉到了 夜夜夜夜 .
13年到北京务工, 遇到一批批NB的同龄人物. 一块工作, 一块喜欢锻炼, 一块默默的学习.
从他(她)们身上发现一个事实.
假如我们一样聪明,
当你抱怨自己为什么努力了, 确还是 这么水的时候 ; 其实他(她)们在拼命. 而你只是在努力 ,
假如我们不一样聪明,
如果还不能开挂, 那会是怎么样精彩 x x x x.
前言 - 内存越界处理
我们先看设计图. 内存越界检查原理如下
上面原理是不是很简单. 而这恰恰是最通用的做法. 美的东西不负责. 美很重要.
那我们按照上面设计思路. 首先构建 接口文件 checkmem.h
#ifndef _H_MEMCHECK_CHECKMEM
#define _H_MEMCHECK_CHECKMEM
#include <stddef.h>
/*
* 对malloc进行的封装, 添加了边界检测内存块
* (inline 原本分_DEBUG有宏处理, 后面没加等于没用)
* sz : 申请内存长度
* : 返回得到的内存首地址
*/
extern inline void* mc_malloc(size_t sz);
/*
* 对calloc进行封装, 添加边界检测内存块
* cut : 申请的个数
* sz : 每个的大小
*/
extern inline void* mc_calloc(size_t cut, size_t sz);
/*
* 对relloc进行了封装, 同样添加了边间检测内存块
*/
extern inline void* mc_realloc(void* ptr, size_t sz);
/*
* 对内存检测, 看是否出错, 出错直接打印错误信息
* 只能检测, check_* 得到的内存
*/
extern inline void mc_check(void* ptr);
#endif // !_H_MEMCHECK_CHECKMEM
主要是对 malloc, calloc, realloc 进行添加尾部和头部的内存块处理. 就这么简单一步. 假如能看懂上面设计思路图.
这些代码都可以跳过了. 思路比代码重要. 好那我们继续展现实现部分. checkmem.c
#include "checkmem.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
// 控制台打印错误信息, fmt必须是双引号括起来的宏
#define CERR(fmt, ...) \
fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\r\n",\
__FILE__, __func__, __LINE__, errno, strerror(errno), ##__VA_ARGS__)
//控制台打印错误信息并退出, t同样fmt必须是 ""括起来的字符串常量
#define CERR_EXIT(fmt,...) \
CERR(fmt,##__VA_ARGS__),exit(EXIT_FAILURE)
// 插入字节块的个数
#define _INT_CHECK (1<<4)
/*
* 对malloc进行的封装, 添加了边界检测内存块
* sz : 申请内存长度
* : 返回得到的内存首地址
*/
inline void*
mc_malloc(size_t sz) {
// 头和尾都加内存检测块, 默认0x00
char* ptr = calloc(1, sz + 2 * _INT_CHECK);
if (NULL == ptr) {
CERR_EXIT("malloc sz + sizeof struct check is error!");
}
//前四个字节保存 最后一个内存块地址 大小
size_t* iptr = (size_t*)ptr;
*iptr = sz + _INT_CHECK;
return ptr + _INT_CHECK;
}
/*
* 对calloc进行封装, 添加边界检测内存块
* cut : 申请的个数
* sz : 每个的大小
*/
inline void*
mc_calloc(size_t cut, size_t sz) {
return mc_malloc(cut*sz);
}
/*
* 对relloc进行了封装, 同样添加了边间检测内存块
*/
inline void*
mc_realloc(void* ptr, size_t sz) {
// 先检测一下内存
mc_check(ptr);
// 重新申请内存
char* cptr = (char*)ptr - _INT_CHECK;
char* nptr = calloc(1, sz + 2 * _INT_CHECK);
if (NULL == nptr) {
CERR_EXIT("realloc is error:%p.", ptr);
}
// 内存移动
size_t* bsz = (size_t*)cptr;
memcpy(nptr, cptr, *bsz < sz ? *bsz : sz);
*bsz = sz;
free(cptr);
return nptr;
}
// 检测内存是否错误, 错误返回 true, 在控制台打印信息
static void _iserror(char* s, char* e) {
while (s < e) {
if (*s) {
CERR_EXIT("Need to debug test!!! ptr is : (%p, %p).check is %d!",s, e, *s);
}
++s;
}
}
/*
* 对内存检测, 看是否出错, 出错直接打印错误信息
* 只能检测, check_* 得到的内存
*/
inline void
mc_check(void* ptr) {
char *sptr = (char*)ptr - _INT_CHECK;
//先检测头部
char* s = sptr + sizeof(size_t);
char* e = sptr + _INT_CHECK;
_iserror(s, e);
//后检测尾部
size_t sz = *(size_t*)sptr;
s = sptr + sz;
e = s + _INT_CHECK;
_iserror(s, e);
}
代码实现都很中规中矩, 比较容易. 也就百行. 按照接口文件一个个看实现. 很容易学到开发中技巧. 提高实战技巧.
扯一点, C, C++ 老开发人员水平都比较高, 不喜欢写注释. 这个强烈推荐不是大牛的选手一定要多写注释.
不要扯 什么 <代码即注释> . 多写注释容易加深自己二次思考, 加快自己的成长. 不要和老开发人学这个 , 如果你跳槽, 遇到一个大项