C语⾔⼤型程序的项⽬管理与实现
C语⾔⼤型程序的项⽬管理与实现
当程序复杂时源代码会很长,如果把全部代码放在⼀个源⽂件⾥,写程序,修改、加⼯程序都会很不⽅便。程序⽂件很⼤时,装⼊编辑会遇到困难;在⽂件中位置也不⽅便;对程序做了⼀点修改,调试前必须对整个源⽂件重新编译;如果不慎把已经调试确认的正确部分改了,⼜会带来新的⿇烦。在实践中⼈们体会到:应当把⼤软件(程序)的代码分成⼀些部分,分别放在⼀组源程序⽂件中,分别进⾏开发、编译、调试,然后把它们组合起来,形成整个软件(程序)。C语⾔本⾝⽀持这种开发⽅式。当我们写的程序较⼤时,上述问题就会反应出来,因此应当学习“⼤程序”的开发⽅法。
把⼀个程序分成⼏个源程序⽂件,显然这些源⽂件不是互相独⽴的。⼀个源⽂件⾥可能使⽤其他源⽂件定义的程序对象(外部变量、函数、类型等),这实际上在不同源⽂件间形成了⼀种依赖关系。这样,⼀个源⽂件⾥某个程序对象的定义改动时,使⽤这些定义的源⽂件也可能要做相应修改。在⽣成可执⾏程序时,应该重新编译改动过的源⽂件,⽽没改过的源⽂件就不必编译了。在连接⽣成可执⾏程序时,要把所有必要的模块装配在⼀起。这些管理⼯作可以由⼈⾃⼰做,但是很⿇烦。 TURBO C集成开发环境的项⽬管理功能能帮助我们处理这些问题。利⽤这种功能,开发⼤程序的⼯作将更加⽅便。今天的各种程序开发环境都提供了类似的管理功能。
⽤C语⾔写⼤程序,应当把源程序分成若⼲个源⽂件。其中有:
(1)⼀个或⼏个⾃定义的头⽂件,通常⽤ .h 作为扩展名。头⽂件⾥⼀般放:
#include预处理命令,引⽤系统头⽂件和其他头⽂件;
⽤#define定义的公共常量和宏;
数据类型定义,结构、联合等的说明;
函数原型说明,外部变量的extern说明;等等。
(2)⼀个或⼏个程序源⽂件,通常⽤ .c 作为扩展名。这些⽂件中放:
对⾃定义头⽂件的使⽤(⽤#include命令);
源⽂件内部使⽤的常量和宏的定义(⽤#define命令);
外部变量的定义;
各函数的定义,包括main函数和其他函数。
不提倡在⼀个 .c ⽂件⾥⽤#include命令引⼊另⼀个 .c ⽂件的做法。这样往往导致不必要的重新编译,在调试程序查错时也容易引起混乱。应该通过头⽂件⾥的函数原型说明和外部变量的extern说明,建⽴起函数、外部变量的定义(在某个源程序⽂件中)与它们的使⽤(可能在另⼀个源程序⽂件中)之间的联系,这是正确的做法。
来源: < >
⼤型项⽬中C语⾔的模块化建议
⼀个⼤型的软件项⽬通常包含很多复杂的功能,实现这个项⽬不是⼀个程序员单匹马可以胜任的,往往 需要⼀个团队的有效分⼯合作,另外,在⼀个以C代码为主的完整的项⽬中,经常也需要加⼊⼀些其他语⾔的代码,例如,C代码和汇编代码的混合使⽤,C⽂件和
C++的同时使⽤。这些都增加了⼀个软件项⽬的复杂程度,为了提⾼软件质量,合理组织的各种代码和⽂件是⾮常重要的。组织代码和⽂件的⽬的是为了使团队合作更加有效, 使软件项⽬有良好的可扩展性、可维护性、可移植性、可裁减、可测试性,防⽌错误发⽣,提⾼软件的稳定性。
软件项⽬通常采⽤ 层次化结构开发和模块化开发,例如,⼀个嵌⼊式软件项⽬可能有驱动层,操作系统层,功能层,应⽤程序层,每⼀个层使⽤它的 下层提供的接⼝,并为它的上层提供调⽤接⼝;模块则是
每⼀个层中完成⼀个功能的单元,例如驱动层的每⼀个设备的驱动就是⼀个模块,应⽤层的每个应⽤程序就是⼀个模块,模块使⽤ 下层提供的接⼝和 同层其他模块提供的接⼝,完成特定功能,为上层和同层的其他模块提供调⽤接⼝。
这⾥的接⼝是指⼀个功能模块暴露出来的,提供给其他模块的访问具体功能的⽅法。 根据C语⾔的特点,使⽤*.c⽂件实现模块的功能,
使⽤*.h⽂件暴露单元的接⼝,在*.h⽂件⾥声明外部其他模块可能是⽤的函数,数据类型,全局变量,类型定义,宏定义和常量定义.外部模块只需包含*.h⽂件就可以使⽤相应的功能.当然,模块可以在细化为⼦模块.虽然我们这⾥说的接⼝和COM(通⽤组件模型)⾥定义的接⼝不同,但是,根据COM⾥对接⼝的讨论, 为了使软件在修改时,⼀个模块的修改不会影响到其他模块的⼀个模块的修改不会导致其他模块也需要修改,所以,接⼝第⼀次发布后,修改*.h⽂件不能导致使⽤这个接⼝的其他模块需要重新编写.
⽂件组织的基本建议
使⽤层次化和模块化的软件开发模型. 每⼀个模块只能使⽤所在层和下⼀层模块提供的接⼝.
每个模块的⽂件包存在独⽴的⼀个⽂件夹中.通常情况下,实现⼀个模块的⽂件不⽌⼀个,这些相关的  ⽂件应该保存在⼀个⽂件夹中.
⽤于模块裁减的条件编译宏保存在⼀个独⽴的⽂件⾥,便于软件裁减。
硬件相关代码和操作系统相关代码与纯C代码相对独⽴保存,以便于软件移植.
声明和定义分开,使⽤*.h⽂件暴露模块需要提供给外部的函数,宏,类型,常量,全局变量,尽量做到模块对外部透明,⽤户在使⽤模块功能时不需要了解具体的实现,⽂件⼀旦发布,要修改⼀定要很慎重,
⽂件夹和⽂件命名要能够反映出模块的功能,所以命名要⽤意义的名字。
正式版本和测试版本使⽤统⼀⽂件,使⽤宏控制是否产⽣测试输出。
必要的注释不可缺少,提⾼⽂件的可读性。
头⽂件建议参考以下的规则
头⽂件中不能有可执⾏代码,也 不能有数据的定义,只能有宏、类型(typedef,struct,union, menu  enum),数据和函数的声明。
例如以下的代码可以包含在头⽂件⾥:
# define      NAMESTRING      “name”
typedef      unsign  long      word;
enum{
flag1;
flag2;
};
typedef struct
{
int      x;
int      y;
}Piont;
extent      Fun( void);
extent      int      a;
全局变量和函数的定义不能出现在*.h⽂件⾥。例如下⾯的代码不能包含在头⽂件:
int    a;
void    Fun1( void)
{
a ++;
}
头⽂件中不能包 本地数据(模块⾃⼰使⽤的数据或函数,不被其他模块使⽤)。这⼀点相当于⾯向对象程序设计⾥的私有成员,即只有模块⾃⼰使⽤的函数,数据,不要⽤extent在头⽂件⾥声明, 只有模块⾃⼰使⽤的宏,常量,类型也不要在头⽂件⾥声明,应该在⾃⼰的*.c ⽂件⾥声明。
含⼀些需要使⽤的声明。在头⽂件⾥声明外部需要使⽤的数据,函数,宏,类型。
防⽌被重复包含。使⽤下⾯的宏防⽌⼀个头⽂件被重复包含。
# ifndef      MY_INCLUDE_H
# define      MY_INCLUDE_H
<;头⽂件内容>
# endif
包含extern  "C",使 的 得程序可以在C++编译器被编译
# ifdef      __cplusplus
extern"C"{
# endif
<;函数声明>
# ifdef      __cplusplus
}
编写c语言的软件#enfif
  被extern "C"修饰的变量和函数是按照C语⾔⽅式编译和连接的;未加extern“C”声明时的编译⽅式,作为⼀种⾯向对象的语
⾔,C++⽀持函数重载,⽽过程式语⾔C则不⽀持。函数被C++编译后在符号库中的名字与C语⾔的不同。例如,假设某个函数的原型
为:
void foo(int  x,int y);该函数被C编译器编译后在符号库中的名字为_foo,⽽C++编译器则会产⽣像_foo_int_int之类的名字(不同的编译器可能⽣成的名字不同,但是都采⽤了相同的机制,⽣成的新名字称为“mangled      name”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void  foo(int  x,int  y)与void  foo(int x,float y)编译⽣成的符号是不相同的,后者为_foo_int_float。 同样地,C++中的变量除⽀持局部变量外,还⽀持类成员变量和全局变量。⽤户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。⽽本质上,编译器在进⾏编译时,与函数的处理相似,也为类中的变量取了⼀个独⼀⽆⼆的名字,这个名字与⽤户程序中同名的全局变量名字不同。加extern "C"声明后的编译和连接,强制C++连接器按照C编译器产⽣的符号_foo链接。
结合起来就是:
# ifndef      MY_INCLUDE_H
# define      MY_INCLUDE_H
# ifdef      __cplusplus
extern"C"{
# endif
<;函数声明>
# ifdef      __cplusplus
}
#enfif
# endif
保证在使⽤这个头⽂件时,⽤户不⽤再包含使⽤此头⽂件的其他前提头⽂件,即要使⽤的头⽂件已经包含在此头⽂件⾥。例如:area.h头⽂件包含了⾯积相关的操作,要使⽤这个头⽂件不需同时包含了关于点操作的头⽂件piont.h。⽤户在使⽤area.h时不需要⼿动包含piont.h,因为我们已经在 area.h中⽤#include  “point.h”包含了这个头⽂件。
⽤来暴露接⼝的头⽂件还需要参考更多的规则:
1, ⼀个模块⼀个接⼝,不能⼏个模块⽤⼀个接⼝。
2, ⽂件名为和实现模块的c⽂件相同。abc.c--abc.h
3,尽量不要使⽤extern来声明⼀些共享的数据。因为这种做法是不安全的,外部其他模块的⽤户可能不能完全理解这些变量的含义,最好提供函数GetPut访问这些变量。
4, 尽量避免包含其他的头⽂件,除⾮这些头⽂件是独⽴存在的。这⼀点的意思是,在作为接⼝的头⽂件中,尽量不要包含其他模块的那些暴露*.C⽂件中内容的头⽂件,但是 可以包好含⼀些不是⽤来暴露接⼝的头⽂件。
5, 不要包含那些只有在可执⾏⽂件中才使⽤的头⽂件,这些头⽂件应该在*.c⽂件中包含。这⼀点如同上⼀点,为了提⾼接⼝的独⽴性和透明度。
6, 接⼝⽂件要有⾯向⽤户的充⾜的注释。从应⽤⾓度描述个暴露的内容。
7, 接⼝⽂件在发布后尽量避免修改,即使修改也要保证不影响⽤户程序。
多个代码⽂件使⽤⼀个接⼝⽂件:这种头⽂件⽤于那些认为⼀个模块使⽤⼀个⽂件太⼤的情况。增加以下建议。
1, 多个代码⽂件组成的⼀个模块只有⼀个接⼝⽂件。因为这些⽂件完成的是⼀个模块。
2, 使⽤模块下⽂件命名 <;系统名 > <;模块名命名>      // ov7620_reg.h
3,不要滥⽤这种⽂件。
4,有时候也会出现⼏个*.c⽂件⽤于共 向享数据的*.h⽂件,这种⽂件的特点是 在⼀个*.c⽂件⾥定义全局变量,⽽在其他*.c⽂件⾥使⽤,要将这种⽂件和⽤于暴露模块接⼝的⽂件区别。
5,⼀个模块如果有⼏个⼦模块,可以⽤⼀个*.h⽂件暴露接⼝,在这个⽂件⾥⽤ #include包含每个⼦模块的接⼝⽂件。
还有⼀种头⽂件,说明性头⽂件,这种头⽂件不需要有⼀个对应的代码⽂件, 在这种⽂件⾥⼤多包含了⼤量的宏定义,没有暴露的数据变量和函数。这些⽂件给出以下建议:
1,包含⼀些需要的概念性的东西.
2,命名⽅式,定义的功能.h
3,不包含任何其他的头⽂件.
4,不定义任何类型.
5,不包含任何数据和函数声明.
上⾯介绍了C头⽂件的⼀些建议,下⾯介绍C代码⽂件*.c⽂件的⼀些建议,*.c⽂件是C语⾔中⽣成汇编代码和机器码的内容,要注意以下建议:
1.命名⽅式    模块名.c
2, ⽤static修饰本地的数据和函数。
3, 不要使⽤external。这是在*.h中使⽤的,可以被包含进来。    4,⽆论什么时候定义内部的对象,确保独⽴与其他执⾏⽂件。        5,这个⽂件⾥必须包含相应功能函数。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。