C语言编写大规模的程序概要--《C语言程序设计现代方法》

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

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

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

头文件

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

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

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

/**************************************
 * macro_typedef.h                    *
 *                                    *
 * C语言共享宏定义和类型定义          *
 **************************************/

#define N 100
typedef 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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

共享宏和类型定义

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

/**************************************
 * file3.c                            *
 *                                    *
 * 测试同一个类型定义用到一个C程序的多个文件  *
 **************************************/

#include <stdio.h>

#include "macro_typedef.h"

void print()
{
  printf("N = %d,", N);
  BOOL i = 50;
  printf("i = %d\n", i);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

验证类型定义链接

利用头文件共享函数原型

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

/**************************************
 * 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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

共享函数原型

利用头文件共享变量声明

把全局变量的定义定义文件中,同时将变量的声明(使用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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

共享全局变量

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

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

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

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

 
 
  • 1
  • 2
  • 3
  • 4

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

/***************************************
 * file1.h                             *
 *                                     *
 * 使用#ifndef和#endif避免多次包含     *
 ***************************************/

#ifndef _FILE1_H_
#define _FILE1_H_

#define N 20
typedef 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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

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

C语言程序的划分原则

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

大规模C程序的构建

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

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

 
 
  • 1

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

makefile

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

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

 
 
  • 1
  • 2

在创建了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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

Makefile文件:

calc : calculate.o add.o minus.o
    gcc -o calc calculate.o add.o minus.o
calculate.o : calculate.c minus.h add.h
    gcc -c calculate.c
minus.o : minus.c minus.h
    gcc -c minus.c
add.o : add.c add.h
    gcc -c add.c
.PHONEY : clean
clean :
    rm *.o calc
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

外部定义宏

参考文献

  1. K.N. King 著,吕秀峰 译. C语言程序设计-现代方法. 人民邮电出版社
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值