- 编写 C源程序
- 编译预处理:包含头文件、宏定义及宏展开、条件编译、特殊符号处理
- 编译
- 优化程序
- 汇编程序
- 链接程序:
- 生成可执行文件
按照业务逻辑和 C语言的语法规则编写源代码。
文件包含:一个源文件可以使用#include
指令,将另一个源文件的全部内容包含到本文件中,插入到当前的位置。
#include <abc.h> //标准方式(到存放 C 库函数头文件的目录中寻找要包含的文件)
#include "abc.h" //先在用户当前目录寻找要包含的文件,找不到再按标准方式查找
常用的库函数头文件:
函数 | 头文件 |
---|---|
数学函数 | math.h |
字符串函数 | string.h |
字符函数 | ctype.h |
输入输出函数 | stdio.h |
动态分配内存函数 | stdlib.h 或 malloc.h(和C编译系统有关) |
用一个指定的标识符(宏名)代表一个字符串,宏名习惯大写,可读性要好(见名知义),尽量集中放到源程序的最前面。使用宏是为了提高程序的通用性(一改全改)、避免重复书写某些字符串。一般用宏来代替简短的表达式。有的问题,用宏和函数都可以。善用宏定义可以简化程序。
将宏名替换成字符串的过程。宏展开仅做简单的置换,而不做正确性检查。
- 不带参数的宏定义
#define 标识符 字符串 //不带参数的宏定义
#define PI 3.1415926 //定义一个宏 PI 代替字符串 3.1415926
#define VERSION "Version 5.3 Copyright(c) 2020" //使用宏代替一个字符串
#define COUNT 25
#define PRICE 9.9
#define TOTAL (COUNT * PRICE) //宏定义可以嵌套(可以层层置换)
- 带参数的宏定义
#define 标识符(参数表) 字符串 //带参数的宏定义(除了进行字符串替换,还有进行参数替换。)
#define PRINT(x) printf(""%d\n",x); //带参数的宏
#define SQUARE(x) ((x)*(x)) //带参数的宏:通过括号保证宏和参数的完整性
- 带参数的宏可能产生副作用
int a = 11;
SQUARE(a++) //本意是想计算 11*11 后再自增 a,但实际进行的计算是 11*12
SQUARE(a)
a++; //解决:把可能产生副作用的代码移动到宏调用之外即可
注:
#
为字符串化运算符。##
运算符用于把两个参数连接到一起。#undef
用于终止宏定义的作用域,即取消前面定义的宏定义。
条件编译的作用是提高 C 源程序的通用性,以及方便调试,可根据需要选用不同的形式。
若所指定的标识符已经被#define
指令定义过,则在程序编译阶段对程序段 1 进行编译;否则编译程序段 2。
#ifdef 标识符 //ifdef 表示 if defined
程序段 1 //语句组或指令行
#else
程序段 2 //可以没有 #else 的部分
#endif
若所指定的标识符未被#define
指令定义过,则在程序编译阶段对程序段 1 进行编译;否则编译程序段 2。
#ifndef 标识符 //ifndef 表示 if !defined
程序段 1
#else
程序段 2
#endif
当指定的表达式为真(非零)时就编译程序段 1,否则编译程序段 2。
#if 表达式 //可以事先给定条件,使程序在不同的条件下执行不同的功能。
程序段 1
#else
程序段 2
#endif
【例1】使源程序适用于不同类型的计算机系统
#ifdef COMPUTER_A //如果已定义过COMPUTER_A
#define INTEGER 16 //编译此指令行
#else //否则
#define INTEGER_SIZE 32 //编译此指令行
#endif
【例2】方便调试(开关作用,调试完之后在源代码中删去或注释掉#define DEBUG
即可)
#ifdef DEBUG //若前面有#define DEBUG 指令则输出,以便调试分析
printf("Print something useful to the console.\n");
#endif
【例 3】防止重复包含头文件:如果已经包含了头文件 abc.h 的内容,则不再包含该头文件,否则包含该头文件的内容。
//stdio.h
#ifndef _STDIO_H_
#define _STDIO_H_
...//头文件内容
#endif
【例】计算并输出前 10 个Fibonacci数。涉及的2个源文件内容如下:
//源代码文件 1 main.c
#include <stdio.h>
int Fib(int n);
int main() {
int n;
printf("Fibonacci Series:");
for(n = 0;n < 10;n++)
printf("%d ", Fib(n));
printf("\n");
return 0;
}
//源代码文件 2 Fib.c
int Fib(int n) {
if( n < 2 ) return n == 0 ? 0 : 1; //边界条件
else return Fib(n - 1) + Fib(n - 2); //递归表达式
}
在命令行中依次执行下述命令(分别编译后再链接),输出结果为Fibonacci Series:0 1 1 2 3 5 8 13 21 34
。
# 编译 main.c
gcc -c main.c -o main.o
# 编译 fib.c
gcc -c fib.c -o fib.o
# 链接
gcc main.o fib.o -o fib.exe
# 执行
./fib.exe
注:编译预处理、编译、优化程序、汇编程序这几个阶段统称为编译阶段,而链接阶段是指把链接程序生成可执行文件的过程。