C语言好的编码风格

来源:互联网 发布:dots像素软件 编辑:程序博客网 时间:2024/06/05 02:25

多用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