华为C语言软件编程规范和范例 下载本文

{

PERSON_BASE_INFO person_base; PERSON_ADDRESS person_addr; } PERSON;

?5-7 :仔细设计结构中元素的布局与排列顺序,使结构容易理解、节省占用空间,并减少引起误用现象

说明:合理排列结构中元素顺序,可节省空间并增加可理解性。 示例:如下结构中的位域排列,将占较大空间,可读性也稍差。 typedef struct EXAMPLE_STRU {

unsigned int valid: 1; PERSON person;

unsigned int set_flg: 1; } EXAMPLE;

若改成如下形式,不仅可节省1字节空间,可读性也变好了。 typedef struct EXAMPLE_STRU {

unsigned int valid: 1; unsigned int set_flg: 1; PERSON person ; } EXAMPLE;

?5-8 :结构的设计要尽量考虑向前兼容和以后的版本升级,并为某些未来可能的应用保留余地(如预留一些空间等)

说明:软件向前兼容的特性,是软件产品是否成功的重要标志之一。如果要想使产品具有较好的前向兼容,那么在产品设计之初就应为以后版本升级保留一定余地,并且在产品升级时必须考虑前一版本的各种特性。

?5-9 :留心具体语言及编译器处理不同数据类型的原则及有关细节

说明:如在C语言中,static局部变量将在内存“数据区”中生成,而非static局部变量将在“堆栈”中生成。这些细节对程序质量的保证非常重要。

?5-10 :编程时,要注意数据类型的强制转换

说明:当进行数据类型强制转换时,其数据的意义、转换后的取值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。

?5-11 :对编译系统默认的数据类型转换,也要有充分的认识 示例:如下赋值,多数编译器不产生告警,但值的含义还是稍有变化。 char chr;

unsigned short int exam; chr = -1;

exam = chr; // 编译器不产生告警,此时exam为0xFFFF。

?5-12 :尽量减少没有必要的数据类型默认转换与强制转换

?5-13 :合理地设计数据并使用自定义数据类型,避免数据间进行不必要的类型转换

?5-14 :对自定义数据类型进行恰当命名,使它成为自描述性的,以提高代码可读性。注意其命名方式在同一产品中的统一

说明:使用自定义类型,可以弥补编程语言提供类型少、信息量不足的缺点,并能使程序清晰、简洁。

示例:可参考如下方式声明自定义数据类型。 下面的声明可使数据类型的使用简洁、明了。 typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned int DWORD;

下面的声明可使数据类型具有更丰富的含义。

typedef float DISTANCE; typedef float SCORE;

?5-15 :当声明用于分布式环境或不同CPU 间通信环境的数据结构时,必须考虑机器的字节顺序、使用的位域及字节对齐等问题

说明:比如Intel CPU与68360 CPU,在处理位域及整数时,其在内存存放的“顺序”正好相反。

示例:假如有如下短整数及结构。 unsigned short int exam; typedef struct EXAM_BIT_STRU

{ /* Intel 68360 */ unsigned int A1: 1; /* bit 0 7 */ unsigned int A2: 1; /* bit 1 6 */ unsigned int A3: 1; /* bit 2 5 */ } EXAM_BIT;

如下是Intel CPU生成短整数及位域的方式。

内存: 0 1 2 ... (从低到高,以字节为单位) exam exam低字节 exam高字节

内存: 0 bit 1 bit 2 bit ... (字节的各“位”) EXAM_BIT A1 A2 A3 如下是68360 CPU生成短整数及位域的方式。

内存: 0 1 2 ... (从低到高,以字节为单位) exam exam高字节 exam低字节

内存: 7 bit 6 bit 5 bit ... (字节的各“位”) EXAM_BIT A1 A2 A3 说明:在对齐方式下,CPU的运行效率要快得多。

示例:如下图,当一个long型数(如图中long1)在内存中的位置正好与内存的字边界对齐时,CPU存取这个数只需访问一次内存,而当一个long型数(如

图中的long2)在内存中的位置跨越了字边界时,CPU存取这个数就需要多次访问内存,如i960cx访问这样的数需读内存三次(一个BYTE、一个SHORT、一个BYTE,由CPU的微代码执行,对软件透明),所有对齐方式下CPU的运行效率明显快多了。

1 8 16 24 32 ------- ------- ------- ------- | long1 | long1 | long1 | long1 | ------- ------- ------- ------- | | | | long2 | ------- ------- ------- -------- | long2 | long2 | long2 | | ------- ------- ------- -------- | ....

〔六〕 =====[ 函数、过程 ]=====

16-1 :对所调用函数的错误返回码要仔细、全面地处理

16-2 :明确函数功能,精确(而不是近似)地实现函数设计

16-3 :编写可重入函数时,应注意局部变量的使用(如编写C/C++ 语言的可重入函数时,应使用auto 即缺省态局部变量或寄存器变量)

说明:编写C/C++语言的可重入函数时,不应使用static局部变量,否则必须经过特殊处理,才能使函数具有可重入性。

16-4 :编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P 、V 操作)等手段对其加以保护

说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。

示例:假设Exam是int型全局变量,函数Squre_Exam返回Exam平方值。那么如下函数不具有可重入性。

unsigned int example( int para ) {

unsigned int temp; Exam = para; // (**) temp = Square_Exam( ); return temp; }

此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使Exam赋与另一个不同的para值,所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。此函数应如下改进。

unsigned int example( int para ) {

unsigned int temp;

[申请信号量操作] // 若申请不到“信号量”,说明另外的进程正处于

Exam = para; // 给Exam赋值并计算其平方过程中(即正在使用此

temp = Square_Exam( ); // 信号),本进程必须等待其释放信号后,才可继

[释放信号量操作] // 续执行。若申请到信号,则可继续执行,但其

// 它进程必须等待本进程释放信号量后,才能再使