本帖最后由 mindshare 于 2012-6-3 22:40 编辑
因为自己这几年来一直从事嵌入式系统底层驱动开发,操作系统uc/OS,文件系统维护以及平台搭建工作,在这几年的工作中学习到不少系统方面的经验,现写下来一来是分享给大家,二来是作一个总结,三来是望得到各位大虾的批评指正。现打算从如下几个方面分类总结一下:
(一),预处理
(二),可移植性
(三),警惕潜在危险
(四),对齐与大小端模式
(五),效能问题
(六),重入问题
好像很难得将一个方面的内容一刀切的划分到一个主题中,例如:宏定义既是属于预处理内容,又跟效能有关系,还涉及到潜在危险... 所以,(一),(二),(三)...的划分不一定规范(咱又不是写书 ^_^),且会在不同的地方对某个问题进行不同重点的总结。
============ 预处理 ================
1. 头文件
一般来讲很难保证在一个大型的项目软件中,某个头文件不被重复的引用,例如,在嵌入式系统中往往为了软件的可移植性,都会有一个对所有数据类型的采用宏定义或者typedef定义的头文件,我们假定为portable.h,其内容如下:
#define INT8S char
#define INT8U unsigned char
#define INT16S short
#define INT16U unsigned short
#define INT32S int
#define INT32U unsigned int
类似于这种类型的头文件肯定会被其他的头文件多次引用,假定有component.h 和system.h两个头文件引用了portable.h,而在某个.c文件中引用了component.h和system.h那么编译器在进行预处理的时候会对这个.c文件中引用的所有头文件进行展开,这样portable.h中的宏定义会被多次展开,编译器发现有多次重复定义同一个宏定义的问题,那么编译器会报错/报警告。
为了避免这样的问题,我们对每个头文件加入条件编译。
#ifndef __portable_h__
#define __portable_h__
#define INT8S char
#define INT8U unsigned char
#define INT16S short
#define INT16U unsigned short
#define INT32S int
#define INT32U unsigned int
#endif
这样当,portable.h头文件在某个地方被展开后就会#define __portable_h__,那么在其他的地方有重复引用portable.h时,#ifndef __portable_h__ 是不成立的,所以,就不会对portable.h的内容重复展开了.
2. 条件预处理
#if ,#else, #endif应该是大家经常会用到的。
比如说,有两段代码在前期调试的时候会经常切换使用不同的代码,那么我们可能会写成如下方式:
形式1:
#if 0
gTestCount = a + b;
…….
#else
gTestCount = (a<<1) + b;
……
#endif
这种方式是最简单也最直观的,下面还有几种条件预处理也非常有用,可能大家并不一定熟悉,至少我刚开始写代码的时候就不太熟悉 :)
形式2:
#ifdef __DEBUG_MODE__
#define DEBUG_INFO show_debug_info();
#else
#define DEBUG_INFO
#endif
形式3:
#ifndef __DEBUG_MODE__
#define DEBUG_INFO
#else
#define DEBUG_INFO show_debug_info();
#endif
形式4:
#ifndef __NOT_DEBUG_MODE__
#define DEBUG_INFO
#else
#define DEBUG_INFO show_debug_info();
#endif
也许,有人会觉得上面两种方式都是一样,其实,适时地巧妙地运用会给自己带来很多不必要的麻烦,特别是当这些定义是开放给用户去定义时,往往不可预知用户一定不会定义错误,比如可能用户会将#define __NOT_DEBUG_MODE__不小心
#define __NO_DEBUG_MODE__ 等等。
形式5:
#if __DEBUG_MODE__ = = 1
……
#else
……
#endif
形式6:
#if __DEBUG_MODE__ = = 1
……
#elif __DEBUG_MODE__ = = 2
……
#else
DEBUG MODE DEFINE ERROR!!!!
#endi
形式7:
#if defined __DEBUG_MODE__
……
#elif defined __NOT_DEBUG_MODE__
……
#else
DEBUG MODE DEFINE ERROR!!!!
#endif
3. 宏定义
有时候为了程序(性能/简洁…)的要求,我们往往会在写程序的时候写一些宏代码来满足,这是个好的习惯,但千万不要忘记了所有宏定义一定要加上括号,来保证宏定义在任何地方展开都不会有语法/逻辑错误,语法错误还好,编译器在编译的时候会给你指出来,但是逻辑错误,就……
注意:宏定义在预处理时只是对引用宏的地方做简单的字符替换!
对于表达式型的宏定义一定要加小括号();对于功能型的宏定义一定要加大括号。(不知道这里对于表达式和功能性的两个限定词是否准确 :))
例如:
表达式宏定义:
有如下语句使用宏定义EXPRESS_MACRO c* EXPRESS_MACRO,其本意是c*(a+b)
(1),不加括号
#define EXPRESS_MACRO a+b
c* EXPRESS_MACRO, 展开后变成c*a+b<br /><br />(2), 加括号<br />#define EXPRESS_MACRO (a+b)
c* EXPRESS_MACRO, 展开后变成c*(a+b)
有如下语句使用宏定义FUNCTION_MACRO
if(…)
FUNCTION_MACRO
其本意是
if(…)
{
c=a+b;
d=a*b;
}
(1),不加括号
#define TEST_MACRO c= a+b; d= a*b;
if(…)
FUNCTION_MACRO
展开后变成:
if(…)
c=a+b;
d=a*b;
很严重的逻辑错误…
(2),加括号
#define TEST_MACRO {c= a+b; d= a*b;}
if(…)
FUNCTION_MACRO
展开后变成:
if(…)
{
c=a+b;
d=a*b;
}
本来宏定义的这些问题打算归结到 (三),警惕潜在危险 中去总结的,但考虑到开篇总的多写一些内容,就在这里一并总结了吧 :)
3.#define 与typedef
#define 在编译预处理时进行处理,不会做任何类型检查
typedef 是属于编译的时候处理,编译的时候会做类型检查.br /> |