嵌入式c编程技巧_编程风格

 
目录 :
.编程修养
.编程技巧
.编程风格
 
/*******************************************************
.编程修养
 ----C语言程序写作上的三十二个“修养”
*******************************************************/
 
    ————————————————————————
 
        01、版权和版本
        02、缩进、空格、换行、空行、对齐
        03、程序注释
        04、函数的 [in][out]参数
        05、对系统调用的返回进行判断
        06if 语句对出错的处理
        07、头文件中的 #ifndef
        08、在堆上分配内存
        09、变量的初始化
        10h和c文件的使用
        11、出错信息的处理
        12、常用函数和循环语句中的被计算量
        13、函数名和变量名的命名
        14、函数的传值和传指针
        15、修改别人程序的修养
        16、把相同或近乎相同的代码形成函数和宏
        17、表达式中的括号
        18、函数参数中的 const
        19、函数的参数个数
        20、函数的返回类型,不要省略
        21goto语句的使用
        22、宏的使用
        23static的使用
        24、函数中的代码尺寸
        25typedef的使用
        26、为常量声明宏
        27、不要为宏定义加分号
        28||和&&的语句执行顺序
        29、尽量用 for而不是while做循环
        30、请 sizeof类型而不是变量
        31、不要忽略 Warning
        32、书写 Debug版和Release版的程序
        21goto语究使劲
        22、宏的使用
        23static的使用
        24、函数中的代码尺寸
        25typedef的使用
        26、为常量声明宏
        27、不要为宏定义加分号
        28||和&&的语句执行顺序
        29、尽量用 for而不是while做循环
        30、请 sizeof类型而不是变量
        31、不要忽略 Warning
        32、书写 Debug版和Release版的程序
 
    ————————————————————————
1.版权和版本
file: in top of file as
/************************************************************************
*
*    filenamefilename.c
*
*    description: ...
*
*    author sharp_xiao, (time)2007-04-05
*
*    version number1.0
*
*    amend notes:
*
*
************************************************************************/
 
function: in front function as
/*================================================================
*
* name: XXX
*
* input:
*         type name [IN] : descripts
 
* output:
*    type name [OUT] : descripts
*
* description: 
*         ..............
*
* return:
*        succed:TRUE, failed: FALSE
*
* pop abnormity:
*
* author: sharp_xiao 2007-04-05
*
*
================================================================*/
 
2. 函数指针
首先要理解以下三个问题:
 (1)C语言中函数名直接对应于函数生成的指令代码在内存中的地址,因此函数名可以直接赋给指向函数的指针;
 (2)调用函数实际上等同于 "调转指令+参数传递处理+回归位置入栈",本质上最核心的操作是将函数生成的目标代码的首地址赋给CPU的PC寄存器;
 (3)因为函数调用的本质是跳转到某一个地址单元的 code去执行,所以可以"调用"一个根本就不存在的函数实体;
eg.
   typedef void (*lpFunction) ( ); /* 定义一个无参数、无返回类型的 */
   /* 函数指针类型 */
   lpFunction lpReset = (lpFunction)0xF000FFF0; /* 定义一个函数指针,指向*/
   /* CPU启动后所执行第一条指令的位置 */
   lpReset(); /* 调用函数 */
 
3.数组 vs.动态申请
 (1)尽可能的选用数组,数组不能越界访问 (真理越过一步就是谬误,数组越过界限就光荣地成全了一个混乱的嵌入式系统);
 (2)如果使用动态申请,则申请后一定要判断是否申请成功了,并且 malloc和free应成对出现!即"谁申请,就由谁释放"原则。不满足这个原则,会导致代码的耦合度增大;
eg.
Change:
    char * function(void)
   {
   char *p;
   p = (char *)malloc(…);
   if(p==NULL)
   … ;
   … /* 一系列针对p的操作 */
   return p;
   }
   ...
   char *q = function();
  …
   free(q);
To:
   char *p=malloc(…);
   if(p==NULL)
  … ;
   function(p);
  …
   free(p);
   p=NULL;
  
  
   void function(char *p)
   {
  … /* 一系列针对p的操作 */
   }
 
4.关键字 const
 (1)关键字 const的作用是为给读你代码的人传达非常有用的信息。例如,在函数的形参前添加const关键字意味着这个参数在函数体内不会被修改,属于"输入参数"。在有多个形参的时候,函数的调用者可以凭借参数前是否有const关键字,清晰的辨别哪些是输入参数,哪些是可能的输出参数。
 (2)合理地使用关键字 const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改,这样可以减少bug的出现。
 (3)const在 C++语言中则包含了更丰富的含义,而在C语言中仅意味着:"只能读的普通变量",可以称其为"不能改变的变量"(这个说法似乎很拗口,但却最准确的表达了C语言中const的本质),在编译阶段需要的常数仍然只能以#define宏定义!故在C语言中如下程序是非法的:  
   const int SIZE = 10;
   char a[SIZE]; /* 非法:编译阶段不能用到变量/只能用常量定义数组长度 */
 (4)In C++
class A
{
 const int SIZE = 100; // 错误,企图在类声明中初始化 const 数据成员
 int array[SIZE]; // 错误,未知的 SIZE
};
const 数据成员的初始化只能在类构造函数的初始化表中进行,例如
class A
{
 A(int size); // 构造函数
 const int SIZE ;
};
A::A(int size) : SIZE(size) // 构造函数的初始化表
{
 
}
A a(100); // 对象 a 的SIZE 值为100
A b(200); // 对象 b 的SIZE 值为200
 
5.关键字 volatile
 在变量的定义前加上 volatile关键字可以防止编译器进行优化.
  volatile变量可能用于如下几种情况:
 (1) 并行设备的硬件寄存器(如:状态寄存器,例中的代码属于此类);
 (2) 一个中断服务子程序中会访问到的非自动变量 (也就是全局变量);
 (3) 多线程应用中被几个任务共享的变量。
eg.
Optimize code:
   int a,b,c;
   a = inWord(0x100); /*读取I/O空间0x100端口的内容存入a变量*/
   b = a;
   a = inWord (0x100); /*再次读取I/O空间0x100端口的内容存入a变量*/
   c = a;
As:  
   int a,b,c;
   a = inWord(0x100); /*读取I/O空间0x100端口的内容存入a变量*/
   b = a;
   c = a;
 
6.书写 Debug版和Release版的程序
    #ifdef DEBUG
        void TRACE(char* fmt, ...)
        {
            ......
        }
    #else
        #define TRACE(char* fmt, ...)
    #endif
 
7.尽量用 for而不是while做循环
Change:
    p = pHead;
    p = pHead;
 
    while ( p )
    {
        ...
        p = p->next;
    }
To:
   for ( p=pHead; p; p=p->next )
   {
     ..
   }
 
8.为常量声明宏
Change:
    for ( i=0; i<120; i++)
    {
        ....
    }
To:
    #define MAX_USR_CNT 120
 
    for ( i=0; i
    {
        ....
    }
eg.int user[120]; use macro as the same
 
9.typedef的使用
 一般来说,一个 C的工程中一定要做一些这方面的工作,因为你会涉及到跨平台,不同的平
台会有不同的字长,所以利用预编译和 typedef可以让你最有效的维护你的代码,如下所示:  
    #ifdef SOLARIS2_5
      typedef boolean_t     BOOL_T;
    #else
    #else
      typedef int           BOOL_T;
    #endif
 
    typedef short           INT16_T;
    typedef unsigned short UINT16_T;
    typedef int             INT32_T;
    typedef unsigned int    UINT32_T;
 
    #ifdef WIN32
      typedef _int64        INT64_T;
    #else
      typedef long long     INT64_T;
    #endif
 
    typedef float           FLOAT32_T;
    typedef char*           STRING_T;
    typedef unsigned char   BYTE_T;
    typedef time_t          TIME_T;
    typedef INT32_T         PID_T;  
 使用 typedef的其它规范是,在结构和函数指针时,也最好用typedef,这也有利于程序的
易读和可维护性 .
 
10.函数中的代码尺寸
 一个函数完成一个具体的功能,一般来说,一个函数中的代码最好不要超过 600行左右,越
少越好,最好的函数一般在 100行以内,300行左右的孙函数就差不多了.
 
11.函数的参数个数(多了请用结构)
 一般来说 6个左右.函数参数是从右至左以栈来传递的.
 
12、函数名和变量名的命名
————————————
我看到许多程序对变量名和函数名的取名很草率,特别是变量名,什么 a,b,c,aa,bb,cc,
还有什么 flag1,flag2, cnt1, cnt2,这同样是一种没有“修养”的行为。即便加上好的注
释。好的变量名或是函数名,我认为应该有以下的规则:
 
    1) 直观并且可以拼读,可望文知意,不必“解码”。
    2) 名字的长度应该即要最短的长度,也要能最大限度的表达其含义。
 
    3) 不要全部大写,也不要全部小写,应该大小写都有,如: GetLocalHostName 或是
UserAccount。
    4) 可以简写,但简写得要让人明白,如: ErrorCode -> ErrCode,
ServerListener -> ServLisner, UserAccount -> UsrAcct 等。
    5) 为了避免全局函数和变量名字冲突,可以加上一些前缀,一般以模块简称做为前缀
    6) 全局变量统一加一个前缀或是后缀,让人一看到这个变量就知道是全局的。
    7) 用匈牙利命名法命名函数参数,局部变量。但还是要坚持“望文生意”的原则。
    8) 与标准库(如: STL)或开发库(如:MFC)的命名风格保持一致。
 
13.
———————
版权和版本
———————   
对于 C/C++的文件,文件头应该有类似这样的注释:
/************************************************************************
*
*    文件名: network.c
*
*    文件描述:网络通讯函数集
*
*    创建人: Hao Chen, 2003年2月3
*
*    版本号: 1.0
*
*    修改记录:
*
*
************************************************************************/
 
而对于函数来说,应该也有类似于这样的注释:
 
/*================================================================
*
* 函 数 名: XXX
*
* 参     数:
*
*         type name [IN] : descripts
*
* 功能描述 :
*
*        ..............
*
* 返 回 值:成功 TRUE,失败FALSE
*
* 抛出异常:
*
* 作     者:ChenHao 2003/4/2
*
*
================================================================*/
 
 
 
 
 
 
 
 
/*******************************************************
.编程技巧
 ---(C语言嵌入式系统编程修炼 )
*******************************************************/
1).数据指针
unsigned char *p = (unsigned char *)0xF000FF00;
*p=11;
 
2).函数指针
  首先要理解以下三个问题:
   a.C语言中函数名直接对应于函数生成的指令代码在内存中的地址,因此函数名可以直接赋给指向函数的指针;
   b.调用函数实际上等同于"调转指令+参数传递处理+回归位置入栈",本质上最核心的操作是将函数生成的目标代码的首地址赋给CPU的PC寄存器;
    c.因为函数调用的本质是跳转到某一个地址单元的 code去执行,所以可以"调用"一个根本就不存在的函数实体.
//"软重启 "的作用
186 CPU启动后跳转至绝对地址 0xFFFF0(对应C语言指针是0xF000FFF0,0xF000为段地址,0xFFF0为段内偏移)执行,请看下面的代码:
typedef void (*lpFunction) ( ); /* 定义一个无参数、无返回类型的 */
/* 函数指针类型 */
lpFunction lpReset = (lpFunction)0xF000FFF0; /* 定义一个函数指针,指向 */
/* CPU启动后所执行第一条指令的位置 */
lpReset(); /* 调用函数 */
 
3)数组 vs.动态申请
a.尽可能的选用数组,数组不能越界访问;
b.如果使用动态申请,则申请后一定要判断是否申请成功了,并且 malloc和free应成对出现!
 
4)关键字 const
称其为 "不能改变的变量",在编译阶段需要的常数仍然只能以#define宏定义!
:const int SIZE = 10;
char a[SIZE]; /* 非法:编译阶段不能用到变量 */
 
5)关键字 volatile
volatile变量可能用于如下几种情况:
   a.并行设备的硬件寄存器(如:状态寄存器,例中的代码属于此类);
   b.一个中断服务子程序中会访问到的非自动变量(也就是全局变量);
   c.多线程应用中被几个任务共享的变量。
 
6)使用宏定义
对于宏,我们需要知道三点:  
a.宏定义“像”函数;  
b.宏定义不是函数,因而需要括上所有“参数”;
c.宏定义可能产生副作用。
eg1.#define MIN(A,B) (( A)<= (B) ? (A) : (B) ) //正确
eg2.least = MIN(*p++, b); 
==>( (*p++) <= (b) ?(*p++):(b) ) //发生的事情无法预料
 
7)利用硬件特性
CPU对各种存储器的访问速度,基本上是:  
      CPU内部 RAM > 外部同步RAM > 外部异步RAM > FLASH/ROM
 
8)活用位操作
 
9)浮点变量与零值比较
不可将浮点变量用“ ==”或“!=”与任何数字比较。
eg.    if (x == 0.0) // 隐含错误的比较
应转化为 :
 if ((x>=-EPSINON) && (x<=EPSINON)) //其中 EPSINON 是允许的误差(即精度)
 
10) do not use sizeof(pointer). Because sizeof a pointer is just 4.
eg:
{
   char *ch;
   memset(ch, 0, sizeof(ch)); //there will make some crash issue
}
 
11)notice memset or initialize the variable when define.
 
12)指向类型 T的指针并不等价于类型T的数组.
eg. 在一个源文件里定义了一个数组:
 char a[6];
     则在另外一个文件里进行 extern声明时不能用:
 extern char *a;
     而只能是 : extern char a[];
在使用 extern时候要严格对应声明时的格式.
 
 
--------------------------------------
总结
在性能优化方面永远注意 80-20准备,不要优化程序中开销不大的那80%,这是劳而无功的。
--------------------------------------
 
 
 
/*******************************************************
.编程风格
 ---C语言
*******************************************************/
1.参考 "编程修养"
2.命名规则
1)函数 ->[C++: 类名]用大写字母开头的单词组合而成.
2)局部变量和参数 ->用小写字母开头的单词组合而成.
3)全局变量 ->则使全局变量加前缀g_(表示global).eg.int g_howManyPeople; // 全局变量
 为了避免全局函数和变量名字冲突,可以加上一些前缀,一般以模块简称做为前缀 .
4)表态变量 ->静态变量加前缀s_(表示static)。eg. static int s_initValue; // 静态变量
5)const常量 ->常量全用大写的字母,用下划线分割单词。eg. const int MAX_LENGTH = 100;
6)宏常量 ->一般用大写字母;但可以了解为函数功能时,可以函数的命名规则命名.
7)数据成员 ->[C++]类的数据成员加前缀m_(表示member),这样可以避免数据成员与成员函数的参数同名.
eg.     void Object::SetValue(int width, int height)
 {
 m_width = width;
 m_height = height;
 }
8)标识符应当直观且可以拼读,可望文知意,不必进行“解码” .标识符最好采用英文单词或其组合,便于记忆和阅读.
9)标识符的长度应当符合“ min-length && max-information”原则.
10)命名规则尽量与所采用的操作系统或开发工具的风格保持一致 .
11)程序中不要出现仅靠大小写区分的相似的标识符。 eg. int x, X, z, Z;
12)程序中不要出现标识符完全相同的局部变量和全局变量,尽管两者的作用域不同而不会发生语法错误,但会使人误解 .
13)变量的名字应当使用“名词”或者“形容词+名词”。 eg. { int oldValue, newValue;}
14)全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组) .
[C++: 类的成员函数应当只使用“动词”,被省略掉的名词就是对象本身 ]. eg. DrawBox(); // 全局函数
15)用正确的反义词组命名具有互斥意义的变量或相反动作的函数等 .
16)尽量避免名字中出现数字编号,如 Value1,Value2 等,除非逻辑上的确需要编号。
17)为了防止某一软件库中的一些标识符和其它软件库中的冲突,可以为各种标识符加上能反映软件性质的前缀。例如三维图形标准 OpenGL 的所有库函数均以gl 开头,所有常量(或宏定义)均以GL 开头.
3.
1) if(NULL == XXX) //use value == XXX to avoid XXX = value
   {
 NULL;    //use NULL when do nothing
   }
 
153102701
421126198904103817

你可能感兴趣的:(嵌入式c编程技巧_编程风格)