多用assert(),这样可以定位错误,方便调试
注意:assert是宏,而不是函数。在C的assert.h头文件中。
assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:
#defineassert(expr)\
((expr)\
?__ASSERT_VOID_CAST(0)\
:__assert_fail(__STRING(expr),__FILE__,__LINE__,__ASSERT_FUNCTION))
assert的作用是先计算表达式expr,如果其值为假(即为0),那么它会打印出来assert的内容和__FILE__,__LINE__, __ASSERT_FUNCTION,然后执行abort()函数使kernel杀掉自己并coredump(是否生成coredump文件,取决于系统配置);否则,assert()无任何作用。宏assert()一般用于确认程序的正常操作,其中表达式构造无错时才为真值。完成调试后,不必从源代码中删除assert()语句,因为宏NDEBUG有定义时,宏assert()的定义为空。[1]
请看下面的程序清单badptr.c:
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
int main(void){
FILE* fp;
fp=fopen("test.txt","w");//以可写的方式打开一个文件,如果不存在就创建一个同名文件
assert(fp);//所以这里不会出错
fclose(fp);
fp=fopen("noexitfile.txt","r");//以只读的方式打开一个文件,如果不存在就打开文件失败
assert(fp);//所以这里出错
fclose(fp);//程序永远都执行不到这里来
return 0;
}
[root@localhost error_process]# gccbadptr.c
[root@localhost error_process]# ./a.out
a.out: badptr.c:14: main: Assertion `fp'failed.
如果使用动态链接libc,那么除了__FILE__, __LINE__, __ASSERT_FUNCTION会让目标变的稍稍大了一点,并不会因为多次使用assert()增加目标很多。不过好处也很明显,就是会在assert的地方会打印出来文件名,行数,和函数名。另外,要注意用assert()的错误程度。如果assert()的条件fail了,那么会调用abort()函数让kernel杀掉自己,哪怕用户自己重新注册了SIGABRT信号的行为(abort()会先向自己发送信号SIGABRT保证用户的handler正确执行,然后修改SIGABRT信号的行为为默认行为coredump,再次像自己发送SIGABRT,coredump)。
在调试结束后,可以通过在包含#include <assert.h>的语句之前插入 #defineNDEBUG 来禁用assert调用,示例代码如下:
#include <stdio.h>
#define NDEBUG
#include <assert.h>
使用enum
enum gg_type {
type1,
type2
};
这样就定义了一个类型。这里type1成了一个代表整数0的命名常量。在使用整数常量的任何地方都能使用枚举常量。
然后在使用的时候就enum gg_type type。这里的type是枚举变量。
在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。
C允许对枚举变量使用运算符++,而C++不允许
宏定义的使用
#if defined(DEBUG)
extern int nmallocs;
extern int nfrees;
#define debug_malloc(size) \
({\
void *p = malloc(size); \
nmallocs++; \
printf("malloc() size=0x%lu, %s:%d:%s:p=0x%lx\n", (unsigned long)(size), \
__FILE__, __LINE__, __func__, (unsigned long)p);\
p; \
})
#define malloc(s) debug_malloc(s)
#define free(p) do { \
printf("free() %s:%d:%s:p= 0x%lx\n", __FILE__, __LINE__, \
__func__, (unsigned long)p); \
nfrees++, free(p); \
} while (0)
#define debug_strdup(src) \
({ \
char *dst; \
dst = malloc(strlen(src) + 1); \
strcpy(dst, src); \
dst[strlen(src)] = '\0'; \
dst; \
})
#define strdup(p) debug_strdup(p)
在使用中所有的malloc在预处理阶段都会被改变成 debug_malloc(s)宏多对应的形式。free也一样。在预处理阶段 __FILE__, __LINE__,会被处理成文件名和行号,而 __func__没有变化。他的处理不是在预处理阶段进行的。
#define debug(level, ...) do { \
if (level < TPOOL_DEBUG) {\
flockfile(stdout); \
printf("###%p.%s: ", (void *)pthread_self(), __func__); \
printf(__VA_ARGS__); \
putchar('\n'); \
fflush(stdout); \
funlockfile(stdout);\
}\
} while (0)
<pre name="code" class="cpp">debug(1,"hehehe",100,10.8);//被扩展成
do { if (1 < 3) { flockfile(stdout); printf("###%p.%s: ", (void *)pthread_self(), __func__); printf("hehehe",100,10.8); putchar('\n'); fflush(stdout); funlockfile(stdout); }} while (0);
这里的使用不是很好,在debug后面首先应该是"%s,%d,%f",然后才是真正要打印的东西。因为里面用到了printf。这里__VA_ARGS__代表着后面的一系列参数。