C语言好的编码风格

多用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变量使用在参数上和返回值上。用于约束其的取值范围。

可以使用enum 创建一个”新类型“,并指定他可以具有的值,实际上enum常量是int类型的,因此在可以使用int类型的任何地方都可以使用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__代表着后面的一系列参数。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值