哇塞,C语言有try catch吗?当然没有。倒。。可能有人说了,那你野鬼说没有的东西做什么。
可惜try catch在C标准里面并没有。这里,就通过指针访问的段错误,来设计一个类似try catch的小玩意。希望新手能通过这个写出其他的try catch。
首先我们要动点C标准里,signal和setjmp的东西,以及GNU C的 glibc函数sigsetjmp,siglongjmp。记得要注意哪些是标准里的,哪些不是,这对以后的平台移植很用作用,哪怕你不移植,但万一别人 要移植,因为你没有区分好,导致别人到处找和平台相关的代码,最后这个代码别人用不了,你的代码又有何价值。
先说一下signal这个函数。这函数的作用就是类似对中断向量表的重载,其实是个重定位工作。中断向量表是什么意思,很简单,就是有一个表,表里面有一 堆函数地址,如果对应的中断发生了,就跳转到该中断对应的中断向量表的对应存储区域的函数里(希望看这句话你别跟着念,眼睛也别花,哈)。如果不重定位这 些函数,则会启用默认的函数。例如给打印个消息,然后让进程停止。如果通过signal 对这个中断向量表进行”重载“,则可以进入你指定的函数。而不会进入默认的方式。
简单举例吧,正常的程序,你在bash上执行 ctrl+C,会让程序停止。实际是怎么发生的呢?
1、键盘发现你按键了,则会发出一个信号,除了按键值本身。该信号是告诉CPU,我有事了,CPU的硬件如果不对这个信号视而不见的话,就会告诉OS,哦,有个硬中断发生。
2、此时,OS就对应的发出个软中断,linux下就是 SIGINT。
3、如果对应你当前进程的自己的”中断向量表“是默认情况,则会OS启动一个函数,这个函数会发出一个kill的工作,要求将你这个进程结束。
4、OS此时发现这个动作。就把你的进程给卡擦了。
而你如果用signal“重载”这个中断,则此时原先默认函数不会执行,你会发现,你的程序该做什么还是做什么,除非你的新函数仍然要求kill掉你这个进程。
那么对于段错误,也存在一个中断,是由谁产生的? MMU,硬件产生的。OS获取这个错误之后,就会到对应进程的“中断向量表”找你是否重载过这个对应的中断响应函数,当然这中间还有些其他工作比如中断屏 蔽方面的检查工作等,我就不展开了。由此,如果你写了一个新的函数,则可以不用退出,可以处理些自己的想做的事情。
但是很讨厌的一个问题,如果是你的进程出错,而且出错的理由是对一个不正确的内存空间进行读写操作,无论你执行多少次,这个错误仍然存在。因为你的代码产 生个由MMU发出的段错误的中断,此时你的代码被强行挂起来,就是说现在轮不到你玩了,然后OS处理对应的中断响应,就是你写的代码,而写的那个函数执行 完毕后,如果不kill或者其他动作,你的代码还会被再次执行。那么你的进程会怎么执行呢?在你上次错的地方继续执行。。。。这就郁闷大发了。因为再次产 生个错误,此时又会进入你的中断响应函数。
于是乎,你会发现,你的屏幕在不停的打印东西,如果你的那个函数内有条printf想提醒你,进入了这个函数了。
此时,我们就需要动点手段了。这里要谈一下setjmp ,和longjmp,最终会用到sigsetjmp,siglongjmp,注意,这两玩意不是C标准的,glibc支持,其他的一些C环境也支持,我不一一列出来了。可以查资料。
先说setjmp, longjmp。上下文这个词在OS里,特别进程管理部分经常提到,什么意思呢?简单说就是现场环境,不过只是CPU里面的,包括指令的位置(其实也是在 寄存器里),常规寄存器里的内容(也包括堆栈指针寄存器),和外部存储器就是内存没有关系。
继续举例吧。
假设,剧场正在上演一个话剧,还没结束呢。结果通知,立刻清场,为什么,领导要来开会,你别问为什么,领导就是领导,优先级高,此时你怎么办,那就和观众 商量一下,我们把现场记录下来,台词说哪也记录下来,等领导开完会咱们接着看,此时剧场的情况清点记录完毕,并清理干净,等领导开会,会开完了,再根据被 打断时的场景进行恢复,则此时观众可以连贯的继续看下去。
另一个例子就是香港的赌王片,赌到高潮的时候,无论是正方的叛徒还是反方的卧底,无论是用刀还是用枪,反正把男一号给搞伤了,怎么办?封牌局,拿个罩子, 把桌子罩起来,谁也别想改动这些牌的内容。等男二号上时在继续赌,牌是什么情况,肯定没有变过,无论中间穿插了多少其他镜头。假设刚好这个时候有跑龙套的 要用桌子吃午饭,剧组同意了,把桌子给他用,但是桌子上面的东西则原封不动的挪到别的地方。等男二号来,再把跑龙套的赶走,恢复成前面的牌局。
这里剧场场景的记录并切换成领导的会议桌,以及桌子上的牌局挪动,让给跑龙套的吃午饭,都是叫做上下文切换。setjmp的作用就是保存当前进入 setjmp函数时的环境。同时setjmp返回个值为0。以区别longjmp跳转到当前位置。类似函数调用函数,父函数需要保存的现场工作,否则子函 数退出时,父函数也没办法正常继续工作啊。是不是。当然 setjmp所保存的东西必函数调用时保存的要多一些。其实setjmp没什么特色。我自己都写过对应汇编以实现特定硬件上的需求。就是一堆mov,把寄 存器的值存到指定的位置再返回0。
而longjmp的意思是,可以在你的代码任意的位置,只要setjmp执行过以后的地方,直接跳,跳哪呢?就是跳到setjmp调用时的位置,这个跳哪 的信息从哪得来的,就是setjmp的参数指向的一个BUF,你在这个BUF里面保存了当前地址。因此,如果多次setjmp同一个buf,则在跳到最后 一次,如果每次setjmp了不同的BUF,那么哪个BUF作为longjmp的参数,就是跳到对应setjmp的位置。此时等同于setjmp被返回, 只不过返回值不为0,由此判断是longjmp过来的。继续举例子。
导演说,第N个镜头。。。然后就开始演,演了一半,穿帮了,导演说,停!此时就是longjmp,longjmp去哪?和你演的这段都没关系。直接到当前这个镜头的开始位置,为什么当前镜头可以拍N多次,就是因为你在镜头开始位置做了一个setjmp。
现在说下sigsetjmp siglongjmp。
sigsetjmp ,siglongjmp比setjmp ,longjmp的组合多了个中断屏蔽信息的存储。此时siglongjmp可以恢复到sigsetjmp出现时的中断屏蔽情况。在后面给出的代码的 test10函数中,特地做了一个setjmp ,longjmp的方式,你会发现,第二次出错,并不会进入normal_longjmp函数。因此此时的中断配置等信息并没有对应记录下来,属于出错后 的情况。
OK。现在说说setjmp longjmp有什么好处。
前面说了。signal对SIGSEGV这个中断的响应函数修改后,函数退出,会再次执行MMU发生错误的代码位置,因此我们要确保函数跳过当前出错的位置,执行到我们希望跳过的代码。类似C++的try catch那样,我们希望有try catch。如下
char *p = "1234";
TRY
p[2] = '1';//这显然是错的嘛
CATCH ;//
则我们可以
OK了,为什么这样就可以了呢?因为如果不是longjmp过来的,setjmp始终返回0,则此时必然会执行p[2],如果p[2]不会产生 SIGSEGV的错误,就不会执行longjmp,由此一切照旧,该做什么做什么。如果p[2] = '1'错误,则会发出中断信号 SIGSEGV,而假设我们把 下面SIGNAL_SEGV_DONE这个函数先前用 signal重载过,则此时发生的错误,会导致进入 SIGNAL_SEGV_DONE。如下
注意这里第2个参数是返回的值,就是跳转到setjmp的位置,等同于setjmp返回的值,此时等于上面if的条件不成立,则等于跳出了{}。
而如果新手还是想不同,这个1怎么就被返回到setjmp的地方,而且像函数返回一样呢?我就说两个事情。
1、函数的返回值是放在指定寄存器里的,比如ARM是放在r0里的,子函数把要return的值放在r0 里,返回父函数,则父函数对子函数的返回值直接可以从r0里取得(或者不取,如果不存在返回,或者暂时不需要利用这个函数的返回值)
2、一个函数调用另一个函数,没什么深奥的技巧。就是在返回时把寄存器,包括指令寄存器等等恢复成调用前的情况。唯一是指令寄存器还要再加一下,跳过函数调用的那条指令,然后一个跳转,就回到父函数了。
下面给出代码,我是基于malloc_free上面进行的添加,你可以对比第九部分的代码差异。需要非常明确注意的以下几点
1、这里使用的方式,并不完全等同C++的TRY CATCH.但是机理是一样的。和GDB里面的断点中断也是一样的,只不过后者使用了SEGTRAP 这个中断信号
2、signal在注册函数时只需要一次,你别傻傻的如我的DEMO一样,放在检测的函数里。
3、我这里是为了尽可能只用头文件,所以用了static jmp_buf SIGSEGV_buf;做成全局变量也没有关系。
4、如下,对SIGSEGV_BEGIN 和 SIGSEGV_END的使用一定要加宏判断。我暂时没有想到比较好的解决方案,能把两个宏之间的代码描述能自动预编译剔除掉。
以上代码,等同于
test8是一个不进行中断函数注册的例子,你会发现什么事情都没有发生,和以前一样,因为没注册嘛。
test9是个标准的处理方案。
test10,说过了,让你区分longjmp,setjmp 和siglongjmp sigsetjmp的区别。
你可以通过执行
bin/test_malloc_free_main 10
这里需要重申一下,所谓正向设计下问题检测的开发方法。正向设计时,在错误检测和问题修复的方法是指:
根据源码分析,在源码中加插检测代码的方式,验证对代码的理解和预判是否正确。
而反向跟踪是根据机器执行动作,反向理解逻辑的运行状态,例如GDB。两者很多方面都很像,但存在一个最主要的区别在于,你是在先验的让程序运行判断是否符合先验,还是在程序的运行中理解代码的逻辑。
例如正向设计的问题判断,如果通过try catch来处理,则表示你已经估计到这里是个潜在可能的错误,而通过运行来验证你的判断是否成立。错误的理解和判断是在对源码的分析上,反之,通过 GDB或者其他IDE加断点的跟踪,属于反向检测运行状态来判断逻辑可能哪里出错。可惜try catch在C标准里面并没有。这里,就通过指针访问的段错误,来设计一个类似try catch的小玩意。希望新手能通过这个写出其他的try catch。
首先我们要动点C标准里,signal和setjmp的东西,以及GNU C的 glibc函数sigsetjmp,siglongjmp。记得要注意哪些是标准里的,哪些不是,这对以后的平台移植很用作用,哪怕你不移植,但万一别人 要移植,因为你没有区分好,导致别人到处找和平台相关的代码,最后这个代码别人用不了,你的代码又有何价值。
先说一下signal这个函数。这函数的作用就是类似对中断向量表的重载,其实是个重定位工作。中断向量表是什么意思,很简单,就是有一个表,表里面有一 堆函数地址,如果对应的中断发生了,就跳转到该中断对应的中断向量表的对应存储区域的函数里(希望看这句话你别跟着念,眼睛也别花,哈)。如果不重定位这 些函数,则会启用默认的函数。例如给打印个消息,然后让进程停止。如果通过signal 对这个中断向量表进行”重载“,则可以进入你指定的函数。而不会进入默认的方式。
简单举例吧,正常的程序,你在bash上执行 ctrl+C,会让程序停止。实际是怎么发生的呢?
1、键盘发现你按键了,则会发出一个信号,除了按键值本身。该信号是告诉CPU,我有事了,CPU的硬件如果不对这个信号视而不见的话,就会告诉OS,哦,有个硬中断发生。
2、此时,OS就对应的发出个软中断,linux下就是 SIGINT。
3、如果对应你当前进程的自己的”中断向量表“是默认情况,则会OS启动一个函数,这个函数会发出一个kill的工作,要求将你这个进程结束。
4、OS此时发现这个动作。就把你的进程给卡擦了。
而你如果用signal“重载”这个中断,则此时原先默认函数不会执行,你会发现,你的程序该做什么还是做什么,除非你的新函数仍然要求kill掉你这个进程。
那么对于段错误,也存在一个中断,是由谁产生的? MMU,硬件产生的。OS获取这个错误之后,就会到对应进程的“中断向量表”找你是否重载过这个对应的中断响应函数,当然这中间还有些其他工作比如中断屏 蔽方面的检查工作等,我就不展开了。由此,如果你写了一个新的函数,则可以不用退出,可以处理些自己的想做的事情。
但是很讨厌的一个问题,如果是你的进程出错,而且出错的理由是对一个不正确的内存空间进行读写操作,无论你执行多少次,这个错误仍然存在。因为你的代码产 生个由MMU发出的段错误的中断,此时你的代码被强行挂起来,就是说现在轮不到你玩了,然后OS处理对应的中断响应,就是你写的代码,而写的那个函数执行 完毕后,如果不kill或者其他动作,你的代码还会被再次执行。那么你的进程会怎么执行呢?在你上次错的地方继续执行。。。。这就郁闷大发了。因为再次产 生个错误,此时又会进入你的中断响应函数。
于是乎,你会发现,你的屏幕在不停的打印东西,如果你的那个函数内有条printf想提醒你,进入了这个函数了。
此时,我们就需要动点手段了。这里要谈一下setjmp ,和longjmp,最终会用到sigsetjmp,siglongjmp,注意,这两玩意不是C标准的,glibc支持,其他的一些C环境也支持,我不一一列出来了。可以查资料。
先说setjmp, longjmp。上下文这个词在OS里,特别进程管理部分经常提到,什么意思呢?简单说就是现场环境,不过只是CPU里面的,包括指令的位置(其实也是在 寄存器里),常规寄存器里的内容(也包括堆栈指针寄存器),和外部存储器就是内存没有关系。
继续举例吧。
假设,剧场正在上演一个话剧,还没结束呢。结果通知,立刻清场,为什么,领导要来开会,你别问为什么,领导就是领导,优先级高,此时你怎么办,那就和观众 商量一下,我们把现场记录下来,台词说哪也记录下来,等领导开完会咱们接着看,此时剧场的情况清点记录完毕,并清理干净,等领导开会,会开完了,再根据被 打断时的场景进行恢复,则此时观众可以连贯的继续看下去。
另一个例子就是香港的赌王片,赌到高潮的时候,无论是正方的叛徒还是反方的卧底,无论是用刀还是用枪,反正把男一号给搞伤了,怎么办?封牌局,拿个罩子, 把桌子罩起来,谁也别想改动这些牌的内容。等男二号上时在继续赌,牌是什么情况,肯定没有变过,无论中间穿插了多少其他镜头。假设刚好这个时候有跑龙套的 要用桌子吃午饭,剧组同意了,把桌子给他用,但是桌子上面的东西则原封不动的挪到别的地方。等男二号来,再把跑龙套的赶走,恢复成前面的牌局。
这里剧场场景的记录并切换成领导的会议桌,以及桌子上的牌局挪动,让给跑龙套的吃午饭,都是叫做上下文切换。setjmp的作用就是保存当前进入 setjmp函数时的环境。同时setjmp返回个值为0。以区别longjmp跳转到当前位置。类似函数调用函数,父函数需要保存的现场工作,否则子函 数退出时,父函数也没办法正常继续工作啊。是不是。当然 setjmp所保存的东西必函数调用时保存的要多一些。其实setjmp没什么特色。我自己都写过对应汇编以实现特定硬件上的需求。就是一堆mov,把寄 存器的值存到指定的位置再返回0。
而longjmp的意思是,可以在你的代码任意的位置,只要setjmp执行过以后的地方,直接跳,跳哪呢?就是跳到setjmp调用时的位置,这个跳哪 的信息从哪得来的,就是setjmp的参数指向的一个BUF,你在这个BUF里面保存了当前地址。因此,如果多次setjmp同一个buf,则在跳到最后 一次,如果每次setjmp了不同的BUF,那么哪个BUF作为longjmp的参数,就是跳到对应setjmp的位置。此时等同于setjmp被返回, 只不过返回值不为0,由此判断是longjmp过来的。继续举例子。
导演说,第N个镜头。。。然后就开始演,演了一半,穿帮了,导演说,停!此时就是longjmp,longjmp去哪?和你演的这段都没关系。直接到当前这个镜头的开始位置,为什么当前镜头可以拍N多次,就是因为你在镜头开始位置做了一个setjmp。
现在说下sigsetjmp siglongjmp。
sigsetjmp ,siglongjmp比setjmp ,longjmp的组合多了个中断屏蔽信息的存储。此时siglongjmp可以恢复到sigsetjmp出现时的中断屏蔽情况。在后面给出的代码的 test10函数中,特地做了一个setjmp ,longjmp的方式,你会发现,第二次出错,并不会进入normal_longjmp函数。因此此时的中断配置等信息并没有对应记录下来,属于出错后 的情况。
OK。现在说说setjmp longjmp有什么好处。
前面说了。signal对SIGSEGV这个中断的响应函数修改后,函数退出,会再次执行MMU发生错误的代码位置,因此我们要确保函数跳过当前出错的位置,执行到我们希望跳过的代码。类似C++的try catch那样,我们希望有try catch。如下
char *p = "1234";
TRY
p[2] = '1';//这显然是错的嘛
CATCH ;//
则我们可以
1 |
if
( setjmp (buf)
== 0){ |
2 |
p[2]
= '1' ; |
3 |
} |
OK了,为什么这样就可以了呢?因为如果不是longjmp过来的,setjmp始终返回0,则此时必然会执行p[2],如果p[2]不会产生 SIGSEGV的错误,就不会执行longjmp,由此一切照旧,该做什么做什么。如果p[2] = '1'错误,则会发出中断信号 SIGSEGV,而假设我们把 下面SIGNAL_SEGV_DONE这个函数先前用 signal重载过,则此时发生的错误,会导致进入 SIGNAL_SEGV_DONE。如下
1 |
void
SIGNAL_SEGV_DONE( int
signum){ |
2 |
printf ( "SEG
ERROR !\n" ); |
3 |
longjmp (buf,1); |
4 |
} |
注意这里第2个参数是返回的值,就是跳转到setjmp的位置,等同于setjmp返回的值,此时等于上面if的条件不成立,则等于跳出了{}。
而如果新手还是想不同,这个1怎么就被返回到setjmp的地方,而且像函数返回一样呢?我就说两个事情。
1、函数的返回值是放在指定寄存器里的,比如ARM是放在r0里的,子函数把要return的值放在r0 里,返回父函数,则父函数对子函数的返回值直接可以从r0里取得(或者不取,如果不存在返回,或者暂时不需要利用这个函数的返回值)
2、一个函数调用另一个函数,没什么深奥的技巧。就是在返回时把寄存器,包括指令寄存器等等恢复成调用前的情况。唯一是指令寄存器还要再加一下,跳过函数调用的那条指令,然后一个跳转,就回到父函数了。
下面给出代码,我是基于malloc_free上面进行的添加,你可以对比第九部分的代码差异。需要非常明确注意的以下几点
1、这里使用的方式,并不完全等同C++的TRY CATCH.但是机理是一样的。和GDB里面的断点中断也是一样的,只不过后者使用了SEGTRAP 这个中断信号
2、signal在注册函数时只需要一次,你别傻傻的如我的DEMO一样,放在检测的函数里。
3、我这里是为了尽可能只用头文件,所以用了static jmp_buf SIGSEGV_buf;做成全局变量也没有关系。
4、如下,对SIGSEGV_BEGIN 和 SIGSEGV_END的使用一定要加宏判断。我暂时没有想到比较好的解决方案,能把两个宏之间的代码描述能自动预编译剔除掉。
1 |
#ifdef
__MMDB_FLAG__ |
2 |
SIGSEGV_BEGIN(test)
//there
will be one func ,static void sigsegv_done_test(int signum){ |
3 |
printf ( "you
have a seciton error !\n" ); |
4 |
|
5 |
SIGSEGV_END() |
6 |
#endif |
以上代码,等同于
1 |
static
void
sigsegv_done_test( int
signum){ |
2 |
if
(signum != SIGSEGV){ |
3 |
return ; |
4 |
} |
5 |
//you
todo .... |
6 |
printf ( "you
have a section error !\n" ); |
7 |
siglongjmp(SIGSEGV_buf,1); |
8 |
} |
test9是个标准的处理方案。
test10,说过了,让你区分longjmp,setjmp 和siglongjmp sigsetjmp的区别。
你可以通过执行
bin/test_malloc_free_main 10
的方式调用test10,其他雷同,如下是代码清单。
malloc_free.h
01 |
#ifndef
_malloc_free_H_ |
02 |
#define
_malloc_free_H_ |
03 |
#include
<stdlib.h> |
04 |
#define
ALLOC_PAGE_SIZE 4096 //mininum malloc unit sizes ,not change |
05 |
#define
ALLOC_PAGE_MASK (ALLOC_PAGE_SIZE-1) |
06 |
#define
PAGE_SIZE_ALIGN(n) ((n) + ALLOC_PAGE_MASK) & (~(ALLOC_PAGE_MASK)) |
07 |
#define
MALLOC_NUM_UNIT_SIZE (ALLOC_PAGE_SIZE / sizeof(void*)) |
08 |
#define
MAX_MALLOC_UNIT_NUM 64 //not more than 4096*8 |
09 |
#define
MAX_MALLOC_NUM (MALLOC_NUM_UNIT_SIZE * MAX_MALLOC_UNIT_NUM) // max malloc times ,you can change |
10 |
#define
__MMDB_FLAG__ |
11 |
#ifndef
__MMDB_FLAG__ |
12 |
//c_malloc
c_free means malloc by check,not calloc,not type cmalloc!!!!! |
13 |
#define
c_malloc(a) malloc(a) |
14 |
#define
c_free(a) free(a) |
15 |
#define
MALLOC_FREE_INIT(...) do{}while(0) |
16 |
#define
_TYPE_INDEX_MALLOC_FREE(...) |
17 |
#define
_TYPE_COUNT_MALLOC_FREE(...) |
18 |
#define
CHECK_PTR_RANGE(...) (1) |
19 |
#define
GET_MALLOC_INDEX(...) do{}while (0) |
20 |
#define
CHECK_PTR_RANGE_ER(...) do{}while(0) |
21 |
#if
0 |
22 |
#error
"if not define __MMDB_FLAG__ ,this define how to done " |
23 |
#define
SIGSEGV_BEGIN(...) |
24 |
#define
SIGSEGV_END(...) |
25 |
26 |
#endif
|
27 |
28 |
#define
SIGNAL_SEGV(...) |
29 |
#define
TRY_SEGV(...) |
30 |
#define
CATCH_SEGV(...) |
31 |
32 |
#else |
33 |
#include
<setjmp.h> |
34 |
#include
<signal.h> |
35 |
static
jmp_buf
SIGSEGV_buf; //every
C file have one |
36 |
#define
SIGSEGV_FUNC(name) sigsegv_done_##name |
37 |
#define
SIGSEGV_BEGIN(name) static void SIGSEGV_FUNC(name)(int signum){ if (signum != SIGSEGV) {return;} |
38 |
#define
SIGSEGV_END() siglongjmp(SIGSEGV_buf,1);} |
39 |
#define
SIGNAL_SEGV(name) do {signal(SIGSEGV,SIGSEGV_FUNC(name));}while (0) //no return no need check |
40 |
#define
TRY_SEGV() if (sigsetjmp(SIGSEGV_buf,1) == 0){ |
41 |
#define
CATCH_SEGV(...) } |
42 |
#define
MALLOC_FREE_INIT malloc_free_init |
43 |
#define
_TYPE_COUNT_MALLOC_FREE(name) unsigned long name = 0; |
44 |
#define
_TYPE_INDEX_MALLOC_FREE(name) void ** name; |
45 |
#define
CHECK_PTR_RANGE(p,indexP) ((indexP[0] <= (void *)(p)) && (indexP[1] > (void*)(p))) |
46 |
#define
GET_MALLOC_INDEX(p,indexP) do{indexP = get_malloc_index(p);}while (0) |
47 |
#define
CHECK_PTR_RANGE_ER(p,indexP,n,NAME) do {if (CHECK_PTR_RANGE(p,indexP)){n++;}else{set_check_ptr_range_error_exit(p,indexP,n,NAME);}} while (0) |
48 |
49 |
//ins_inc_file |
50 |
51 |
//ins_typedef_def |
52 |
53 |
//ins_def |
54 |
55 |
//ins_func_declare |
56 |
void
memory_free_init( void *); |
57 |
void
**get_malloc_index( void
*ptr); //not
used in code ,please used GET_MALLOC_INDEX define |
58 |
void
set_check_ptr_range_error_exit( void
*p, void
**index,unsigned long
n, const
char
*str); //not
used in code ,please used CHECK_PTR_RANGE_ER |
59 |
void
*c_malloc( size_t
size); |
60 |
void
c_free( void
*ptr); |
61 |
62 |
#endif |
63 |
64 |
65 |
#endif
//_malloc_free_H_ |
malloc_free.c没有任何变化
一下是test_malloc_free_main.c的清单
001 |
#include
"malloc_free.h" |
002 |
#include
<stdio.h> |
003 |
004 |
typedef
void
(* TEST_FUNC)( void ); |
005 |
#ifdef
__MMDB_FLAG__ |
006 |
SIGSEGV_BEGIN(test)
//there
will be one func ,static void sigsegv_done_test(int signum){ |
007 |
printf ( "you
have a seciton error !\n" ); |
008 |
|
009 |
SIGSEGV_END() |
010 |
#endif |
011 |
static
void
test8( void ){ |
012 |
//test
write to zero point |
013 |
int
*p = 0; |
014 |
//
SIGNAL_SEGV(test); |
015 |
TRY_SEGV(); |
016 |
p[0]
= 0; |
017 |
CATCH_SEGV(); |
018 |
} |
019 |
static
void
test9( void ){ |
020 |
//test
write to zero point |
021 |
int
*p = 0; |
022 |
SIGNAL_SEGV(test); |
023 |
TRY_SEGV(); |
024 |
p[0]
= 0; |
025 |
CATCH_SEGV(); |
026 |
} |
027 |
028 |
#ifdef
__MMDB_FLAG__ |
029 |
jmp_buf
buf1; |
030 |
#endif |
031 |
void
normal_longjmp( int
signum){ |
032 |
printf ( "there
is normal_longjmp func!\n" ); |
033 |
longjmp (buf1,1); |
034 |
} |
035 |
static
void
test10( void ){ |
036 |
#ifdef
__MMDB_FLAG__ |
037 |
038 |
int
*p = 0; |
039 |
signal (SIGSEGV,normal_longjmp); |
040 |
printf ( "test10!\n" ); |
041 |
if
( setjmp (buf1)
== 0){ |
042 |
p[0]
= 0; |
043 |
} else { //
section error ,jmp from normal_longjmp func |
044 |
printf ( "we
come back from normal_longjmp func!\n" ); |
045 |
p[0]
= 0; |
046 |
} |
047 |
#endif
|
048 |
} |
049 |
static
void
test0( void ){ |
050 |
void
*p1 = 0; |
051 |
char
*p2 = 0; |
052 |
printf ( "test0\n" ); |
053 |
p1
=( char *)c_malloc(4); //*4); |
054 |
p2
= ( char *)c_malloc(6); |
055 |
c_free(p1); |
056 |
c_free(p2); |
057 |
return ;
//normal
check |
058 |
} |
059 |
static
void
test1( void ){ |
060 |
char
*p1,*p2; |
061 |
p1
= ( char *)c_malloc(5); |
062 |
p2
= ( char *)c_malloc(6); |
063 |
c_free(p1); |
064 |
//cfree(p2); |
065 |
return ;
//free
lack check |
066 |
} |
067 |
static
void
test2( void ){ |
068 |
char
*p1,*p2,*p3; |
069 |
p1
= ( char *)c_malloc(5); |
070 |
p3
= p2 = ( char *)c_malloc(6); |
071 |
c_free(p1); |
072 |
c_free(p2); |
073 |
c_free(p3); |
074 |
return ;
//free
more check |
075 |
} |
076 |
static
void
test3( void ){ |
077 |
char
**pp = ( char **)c_malloc( sizeof ( char *)*MAX_MALLOC_NUM
+ 1); |
078 |
int
i; |
079 |
for
(i = 0 ; i <= MAX_MALLOC_NUM ; i++){ |
080 |
pp[i]
= ( char *)c_malloc(2); |
081 |
} |
082 |
return ;
//alloc
more check |
083 |
} |
084 |
static
void
test4( void ){ |
085 |
char
*p1,*p2,*p3; |
086 |
p1
= ( char *)c_malloc(5); |
087 |
p2
= ( char *)c_malloc(6); |
088 |
p3
= ( char *)c_malloc(6); |
089 |
c_free(p1); |
090 |
c_free(p2); |
091 |
c_free(p2); |
092 |
return ;
//free
twin check |
093 |
} |
094 |
static
void
test5( void ){ |
095 |
char
*p1,*p2; |
096 |
p1
= ( char *)c_malloc(5); |
097 |
p2
= ( char *)c_malloc(6); |
098 |
c_free(p1); |
099 |
c_free(p2+3); |
100 |
return ;
//free
shift check |
101 |
} |
102 |
static
void
test6( void ){ |
103 |
char
*p1,*p2; |
104 |
p1
= ( char *)c_malloc(5); |
105 |
p2
= ( char *)c_malloc(6); |
106 |
c_free(p1); |
107 |
c_free(0); |
108 |
return ;
//free
zero check |
109 |
} |
110 |
111 |
static
void
test7( void ){ |
112 |
char
*p1,*p2; |
113 |
_TYPE_COUNT_MALLOC_FREE(ptr_count); |
114 |
_TYPE_INDEX_MALLOC_FREE(pindex);
|
115 |
int
i; |
116 |
117 |
p1
= ( char *)c_malloc(5); |
118 |
GET_MALLOC_INDEX(p1,pindex); |
119 |
p2
= p1+ 3; |
120 |
for
(i = 3 ; i< 10 ; i++,p2++){ |
121 |
if
(CHECK_PTR_RANGE(p2,pindex)){ |
122 |
printf ( "p2
bias is %d , check ok\n" ,i); |
123 |
} else { |
124 |
printf ( "error
bias is %d!\n" ,i); |
125 |
} |
126 |
} |
127 |
p2
= p1; |
128 |
for
(i = 0 ; i< 10 ; i++,p2++){ |
129 |
CHECK_PTR_RANGE_ER(p2,pindex,ptr_count, "test7
func p2" ); |
130 |
}
|
131 |
|
132 |
c_free(p1); |
133 |
//c_free(0); |
134 |
return ;
//free
zero check |
135 |
} |
136 |
137 |
#define
TEST_MASK 15 |
138 |
TEST_FUNC
test_a[TEST_MASK+1]= {test0,test1,test2,test3,test4,test5,test6,test7 |
139 |
,test8,test9,test10,test8,test8,test8,test8,test8}; |
140 |
|
141 |
142 |
int
main( int
argc, char
*argv[]){ |
143 |
int
mode; |
144 |
145 |
printf ( "hello
test_malloc_free_main now run...\n" ); |
146 |
MALLOC_FREE_INIT(0); |
147 |
if
(argc < 2){ |
148 |
printf ( "need
parameters!\n" ); |
149 |
return
1; |
150 |
} |
151 |
mode
= atol (argv[1]); //argv[1][0]
- '0'; |
152 |
test_a[mode
& TEST_MASK](); |
153 |
|
154 |
printf ( "hello
test_malloc_free_main now exit...\n" ); |
155 |
return
0; |
156 |
} |
希望新手不要怀疑,为什么我刚学习C,就得碰longjmp和信号方面的知识。没办法啊,本来想先说IPC的,进程之间的通信,采用socket。 但是如果你尝试write一个内容,而对应管道实际已经被关闭,会导致你的write 对应的进程自动退出。上面这些方法不用,我们很难继续下去。
新手你就准备开始多读读计算机组成原理,和操作系统的知识吧。谁让你没事找事要学C呢?活该!!!我没别的话可说了。