编写大规模的C语言程序

来源:互联网 发布:联通网络信号怎么样 编辑:程序博客网 时间:2024/05/01 14:35

为了支持大规模C程序的开发,往往需要把程序分割为一定数量的源文件。C语言的源文件包括两类,一类是实现文件(.c),一类是头文件(.h)。一般地,实现文件主要包括函数和变量的定义,而头文件的作用是在多个定义文件中共享函数原型、宏定义和类型定义、变量声明等信息。

将一个程序分为多个源文件的方式组织程序具有很多好处,比如:

  • 将相关(例如完成同一功能或关于同一事物)的函数和变量放在单独的文件中,便于理解程序结构。
  • 可单独对每个源文件进行编译,避免每次修改都要重新编译整个程序,提高编译速度。
  • 可以将程序的一部分文件用于其他程序,有利于函数重用。

头文件

使用文件包含指令#include为定义文件包含需要的头文件。最好不要在使用#include时包含绝对路径信息,这样可能会造成在不同机器或操作系统的不可移植。头文件自身可以使用#include文件包含指令包含其他头文件。通常将#error放在头文件中用来检查不应该使用该头文件的情况。

利用头文件共享宏定义和类型定义

使用#define的宏定义和typedef的类型定义都可以放在头文件中。这样使得程序易于修改,且避免了不同文件中同一个宏名或类型定义不同导致的问题。

/************************************** * macro_typedef.h                    * *                                    * * C语言共享宏定义和类型定义          * **************************************/#define N 100typedef int BOOL;/************************************** * file1.c                            * *                                    * * 引用共享的宏或类型定义             * **************************************/#include <stdio.h>#include "macro_typedef.h"int main(){  printf("N = %d\n", N);  BOOL i = 10;  printf("i = %d\n", i);  return 0;}/************************************** * file2.c                            * *                                    * * 引用共享的宏或类型定义             * **************************************/#include <stdio.h>#include "macro_typedef.h"int main(){  printf("N = %d\n", N);  BOOL i = 15;  printf("i = %d\n", i);  return 0;}

共享宏和类型定义

同一个类型定义可以出现在一个程序中的多个文件中。

/************************************** * file3.c                            * *                                    * * 测试同一个类型定义用到一个C程序的多个文件  * **************************************/#include <stdio.h>#include "macro_typedef.h"void print(){  printf("N = %d,", N);  BOOL i = 50;  printf("i = %d\n", i);}

验证类型定义链接

利用头文件共享函数原型

可以把函数定义的原型放在头文件中,在所有调用该函数的实现文件中包含该头文件。只将打算被其他定义文件使用的函数原型放在头文件中。

/************************************** * function_define.c                  * *                                    * * C语言中的函数定义                  * **************************************/#include <stdio.h>int Sum(int n){  int i = 1;  int sum = 0;  for (; i <= n; i++)    sum += i;  return sum;}void Print(int n, int sum){  printf("从1到%d的和为%d\n", n, sum);}/*************************************** * function_define.h                   * *                                     * * C语言函数原型声明头文件             * ***************************************/int Sum(int);void Print(int, int);/************************************* * file1.c                           * *                                   * * 使用其他文件定义的函数            * *************************************/#include "function_define.h"int main(){  int n = 10;  Print(n, Sum(n));  return 0;}/************************************* * file2.c                           * *                                   * * 使用其他文件定义的函数            * *************************************/#include "function_define.h"int main(){  int n = 5;  Print(n, Sum(n));  return 0;}

共享函数原型

利用头文件共享变量声明

把全局变量的定义定义文件中,同时将变量的声明(使用extern关键字)放在头文件里。通过包含该头文件就可以在其他文件中访问全局变量。确保变量的声明和定义一致。

/************************************** * global_define.c                    * *                                    * * C语言定义全局变量                  * **************************************/float velocity = 100.0f;void Fast(){  velocity *= 1.1;}void Slow(){  velocity *= 0.9;}/************************************** * global_define.h                    * *                                    * * 定义全局变量声明头文件             * **************************************/extern float velocity;void Fast();void Slow();/*************************************** * file1.c                             * *                                     * * C语言共享全局变量                   * ***************************************/#include <stdio.h>#include "global_define.h"int main(){  Fast();  printf("当前速度: %g\n", velocity);  return 0;}/*************************************** * file1.c                             * *                                     * * C语言共享全局变量                   * ***************************************/#include <stdio.h>#include "global_define.h"int main(){  Slow();  printf("当前速度: %g\n", velocity);  return 0;}

共享全局变量

不要多次包含同一个头文件

如果头文件中只包含宏定义、函数原型或变量声明,则多次包含头文件并不会产生编译错误,但是如果头文件中包含类型定义,那么就会产生编译错误。

为了防止多次包含同一个头文件,可以使用#ifndef#endif指令来将文件内容包裹起来,以保证在一个文件中,这段文件内容只被包含一次。

#ifndef 宏名称#define 宏名称...文件内容#endif

首次包含时,宏应该未定义,所以预处理器保留文件内容,而如果再次包含时,由于宏已被定义,则#ifndef#endif文件内容将被丢弃。

/*************************************** * file1.h                             * *                                     * * 使用#ifndef和#endif避免多次包含     * ***************************************/#ifndef _FILE1_H_#define _FILE1_H_#define N 20typedef int Integer;#endif/************************************** * file1.c                            * *                                    * * 使用file1.h头文件                  * **************************************/#include <stdio.h>#include "file1.h"int main(){  printf("N = %d,", N);  Integer i = 100;  printf("i = %d\n", i);  return 0;}

避免包次包含同一个头文件

C语言程序的划分原则

  • 把相关函数集合放在单独的定义文件中(*.c)。
  • 创建和定义文件名相同的头文件(*.h),并在定义文件中包含该头文件。
  • 将头文件添加到所有需要使用其中声明的函数等信息的文件。
  • 添加程序入口函数main函数,并将其写在一个与应用程序名字相同的定义文件内。

大规模C程序的构建

大多数编译器允许单独一步构建C程序,例如gcc的一般格式为:

gcc -o 可执行文件名 文件1.c 文件2.c ... 文件n.c

编译器首先将这些定义文件编译成目标代码,然后再由链接器将这些代码链接成目标文件,目标文件的名称由-o选项指定。

makefile

当源文件过多时,上面的编译命令将变得非常复杂,而且当一个源文件发生改变时,所有文件都要重新编译。针对这些问题,为了更容易构建大规模的程序,可以利用makefile文件。makefile文件包含了程序部分的文件和文件之间的依赖性。makefile的基本格式如下:

目标 : 依赖文件列表    目标由依赖文件生成命令

在创建了makefile之后,就可以直接使用make工具构建程序。

/************************************** * add.h                              * *                                    * * 加法器,执行加法运算               * **************************************/float add(float, float);/************************************* * add.c                             * *                                   * * 加法器的实现                      * *************************************/#include "add.h"float add(float a, float b){  return a + b;}/************************************** * minus.h                            * *                                    * * 减法器                             * **************************************/float minus(float, float);/************************************** * minus.c                            * *                                    * * 减法器的实现                       * **************************************/#include "minus.h"float minus(float a, float b){  return a - b;}/************************************** * calculate.c                        * *                                    * * 主函数,利用加减法器计算           * **************************************/#include <stdio.h>#include "add.h"#include "minus.h"int main(){  float a, b;  printf("输入两个浮点数:");  scanf("%f%f",&a,&b);  printf("%g和%g的和为%g\n", a, b, add(a, b));  printf("%g和%g的差为%g\n", a, b, minus(a, b));  return 0;}

Makefile文件:

calc : calculate.o add.o minus.o    gcc -o calc calculate.o add.o minus.ocalculate.o : calculate.c minus.h add.h    gcc -c calculate.cminus.o : minus.c minus.h    gcc -c minus.cadd.o : add.c add.h    gcc -c add.c.PHONEY : cleanclean :    rm *.o calc

makefile

多文件链接时可能出现的错误

链接期间经常出现的是无法解决外部引用的错误,可能原因有以下几种:

  • 变量或函数名拼写错误。
  • 连接器找不到指定的文件。
  • 链接器找不到程序中使用的库函数。

重新构建的几种情况

  • 只有一个定义文件修改。只编译该源文件并重新链接程序。
  • 改变头文件。重新编译包含此头文件的所有文件,重新链接程序。

makefile可根据文件的时间自动重新构建,并且按照以上方式,只编译被修改的源文件。

程序外定义宏

C语言的程序需要依赖一些宏的定义,大多数编译器(包括gcc)支持通过选项-D定义宏的值,如果没有指定值则默认为1.同时一些编译器也提供了-U选项,用于取消从外部取消程序中宏的定义。

/*************************************** * external_macro.c                    * *                                     * * C语言使用外部宏                     * ***************************************/#include <stdio.h>int main(){#ifdef DEBUG  printf("现在处于Debug模式\n");#endif#if OPTION == 1  printf("当前选项为1\n");#elif OPTION == 2  printf("当前选项为2\n");#else  #error wrong options#endif}

外部定义宏

参考文献

  1. K.N. King 著,吕秀峰 译. C语言程序设计-现代方法. 人民邮电出版社
0 0