C语言编码积累

来源:互联网 发布:java file.length单位 编辑:程序博客网 时间:2024/05/29 14:04

C语言是嵌入式开发中常用的语言,以下是本人用C语言进行开发过程中的一点小积累。

希望透过分享心得能多和大家交流经验心得,持续更新中...

1. 带参数的宏怎样返回数值?

/* 编写一个判断输入值是否为0的宏,如果为0则返回1,如果不为0则返回0。 */#define IS_ZERO(m)    \({                    \    ((m)==0) ? 1:0;   \})/* 求最大值和最小值的宏 */#define  MAX(x,y) ((x)>(y)) ? (x):(y))#define  MIN(x,y) ((x)<(y)) ? (x):(y)) 

2. 编写方便的调试打印。

         在开发过程中经常需要对代码添加打印进行分析调试,这里提供一个本人常用的调试打印宏。

#define DEBUGMSG_OPEN    1#define MYLOGO "[DebugMsg] "#define FLAG_FILENAME    1#define FLAG_FUNCTION    1#define FLAG_LINE    1#define MY_DEBUG_MSG(fmt,args...)                \{                                                \    if(DEBUGMSG_OPEN){                           \        printf(MYLOGO);                          \                                                 \        if(FLAG_FILENAME)                        \            printf("%s ",__FILE__);              \                                                 \        if(FLAG_FILENAME)                        \            printf("%s ",__FUNCTION__);          \                                                 \        if(FLAG_FILENAME)                        \            printf("%d: ",__LINE__);             \                                                 \        printf(fmt,##args);                      \    }                                            \}

3. 重复free一个指针,会有什么后果?

答案:相当于free一个野指针,后果无法预料。


4. 重新定义数据类型。

         写嵌入式C代码的时候,经常会遇到代码移植的问题。我们通常需要重新定义一些类型,防止由于各种平台和编译器的不同而产生的类型字节数差异。

typedef  unsigned char      boolean;     /* Boolean value type. */typedef  unsigned long int  uint32;      /* Unsigned 32 bit value */typedef  unsigned short     uint16;      /* Unsigned 16 bit value */typedef  unsigned char      uint8;       /* Unsigned 8  bit value */typedef  signed long int    int32;       /* Signed 32 bit value */typedef  signed short       int16;       /* Signed 16 bit value */typedef  signed char        int8;        /* Signed 8  bit value *//* 另外一种写法,同样很方便清晰 */typedef  unsigned char     BYTE;           /* Unsigned 8  bit value type. */typedef  unsigned short    WORD16;         /* Unsinged 16 bit value type. */typedef  unsigned long     WORD32;         /* Unsigned 32 bit value type. */typedef  char   SBYTE;         /* Signed 8  bit value type. */typedef  short  SWORD16;       /* Singed 16 bit value type. */typedef  long   SWORD32;       /* Signed 32 bit value type. *//* 不建议使用这种定义方式 */#define BYTE char#define WORD32 int

5. 关于数据类型的长度的问题

         长整型long  整型int     短整型short 字符型char 浮点型float 双精度型double 指针型8位系统  16位        16位(2字节)            8位(1字节)                            8位(1字节)16位系统 32位(4字节) 16位(2字节) 16位(2字节) 8位(1字节) 32位(4字节)  64位(8字节)   16位(2字节)32位系统 32位(4字节) 32位(4字节) 16位(2字节) 8位(1字节) 32位(4字节)  64位(8字节)   32位(4字节)64位系统 64位(8字节)64位(8字节) 16位(2字节) 8位(1字节) 32位(4字节)  64位(8字节)   64位(8字节)

6. 关于带参数的宏和内联(inline)函数的区别

                   inline函数                                           带参数的宏
                
1)            编译时展开                                        预处理时展开
2)        严格参数类型检查                     不检查参数类型(不带类型)
3)     是否展开由编译器决定                          一定会被展开


7. “!!”一个变量,本人在阅读代码的过程中经常看到 !!var 的这种写法。一开始本人还误以为这是错误或无效代码,后来经过一番百度google之后,发现这种写法实际上是把一个变量作为布尔变量使用。

        在C语言当中,0即为零,非零即为1。因此,如一个变量 a=0x2 ,在 "!a" 之后该变量变为0,在 “!!a” 之后该变量变为1。“ !! ”一个变量最终的结果非0即1,实际上就是布尔代数的取值区间。

        这种写法在位操作频繁的C语言当中非常方便,比用宏或其它表达式来判断一个值是否为0要简洁。就是代码的可读性可能会没那么好。


8.  如何声明一个函数指针数组?函数指针数组在编写任务调度器或任务序列时十分常用,那么如何正确地声明一个函数指针数组呢?

共有两种办法:

/* 办法一 */void (*my_process[256])(int arg1,int arg2);/* 办法二 */typedef void (*my_process)(int arg1,int arg2);my_process array[256];

9. 关于ARM汇编和C语言混合编程中的函数传参,在嵌入式开发过程中,汇编语言和C语言混合编程是很常见的。那么在汇编调用C语言函数时,要如何传递参数呢?

如下图所示:


10. 关于回车符和换行符

Unix系统里,每行结尾只有“<换行>”,即“\n”;
Windows系统里面,每行结尾是“<换行><回车>”,即“\n\r”;
Mac系统里,每行结尾是“<回车>”。
一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。


11.关于带参数的宏和do...while(0),这种写法在Linux内核代码中非常非常常见,既然while(0),为什么还要写它呢?

首先我们来看一段代码:

#include <stdio.h>#define ADD(a,b)   \{                  \  a++;             \  b++;             \}int main(void){    int i=0,a=0,b=0;     if(i==1)          ADD(a,b);    else          printf("Do nothing!!!\n");     return 0;}

看似正确的代码,编译时会出现如下错误:
error C2181: illegal else without matching if

        为什么会出现这样的错误呢?因为我们编写C语言代码时,一般都会在每句后添加分号。上述代码中在ADD()语句后加了一个分号,正是由于这个分号的作用使得else没有与之相对应的if,所以编译出错。我们可以看到上述语句展开后为这样:
    if(i==1)    {   a++;        b++;    }; /* 注意这个分号 */    else       ...
         但是如果使用do{} while(0)进行封装则不会出现这种问题,因此我们在编写带参数的宏时应学会使用do{} while(0)。


11. C语言特性之一:

        请看下面代码,执行后会输出什么?

#include<stdio.h>void function(int a,int b){printf("a =%d\n",a);printf("b =%d\n",b);}int main(void){int a=1;function(a,++a);return 0;}
        答案是a=2;b=2。因为C语言的函数传参,是从右往左扫描的,因此先计算++a,赋予形参2,再给形参1赋值。

12. C语言特性之二:

        请看下面代码,执行后会输出什么?

#include<stdio.h>int main(void){int i,j;        for(i=0,j=0;i<4,j<5;i+=2,j++){printf("i=%d ,j=%d\n",i,j);}return 0;}
        答案是:

i=0 ,j=0
i=2 ,j=1
i=4 ,j=2
i=6 ,j=3
i=8 ,j=4
        因为C语言默认“,”运算符的最右一个元素作为判断条件。

13. 关于编码习惯的小结

摘自《专业嵌入式软件开发》            李云            电子工业出版社

1) 判断失败而非成功                      ——    压缩、简化代码和逻辑,提升代码可读性
2) 采用sizeof减少内存操作失误   ——    主要减少人为判断的失误
3)屏蔽编程语言特性                       ——    主要是考虑到代码移植性的问题
4)恰当地使用goto语句                   ——    用于错误处理,goto语句高效直接   
5) 合理运用数组
6)以逆序方式释放资源                   ——    尤其是错误处理时,经常会遇到
7)在模块对外接口中防范错误
8)避免出现魔数                               ——     所谓的魔数,就是一个纯数字出现在某些代码中,一般用宏来代替。

                                                                           一来是可读性的问题,二来是方便修改调整。
9)利用编程语言特性提高效率
10)复用代码提高维护性
11)借助隐式初始化简单程序逻辑
12)青睐小粒度锁                            ——    这个主要是避免死锁的问题
13)精确包含头文件                        ——    用不到的头文件不要包含进来
14)让模块的对外头文件保持简洁
15)只暴露必要的变量和函数        ——    模块封装的问题
16)尽量消除编译器的所有警告

1 0