C语言目录:
-
编写代码,保存后生成
hello.c
源文件# include<stdio.h> int main(int argc, char const *argv[]){ printf("Hello World!"); return 0; }
-
对源文件
hello.c
进行预处理,生成预处理文件hello.i
-
对预处理文件
hello.i
进行编译,生成汇编文件hello.s
-
对汇编文件
hello.s
进行汇编,生成二进制文件hello.o
-
对二进制文件
hello.o
进行链接,生成可执行文件hello.exe
-
运行可执行文件
hello.exe
7.1 预处理
C编译器在对源程序进行编译前,会将一些特殊指令作解释(如 #include
),进而产生一个新的源程序,之后再进行通常的编译
-
所有的预处理指令都以
#
开始,并且结尾不用分号 -
预处理指令可以出现在程序的任何位置,它的作用范围从它出现的位置到文件尾,所以尽可能将预处理指令写在源程序开头
-
包括:文件包含、宏定义、条件编译
7.1.1 文件包含处理
使用 <>
,仅在系统指定的磁盘路径下搜索所包含的文件
使用 ""
,现在当前工作目录中搜索,若找不到则在系统中寻找
7.1.2 宏定义
宏 :用来替换重复出现的字符串
宏名:代替该字符串的标识符
宏代换(宏展开):在编译预处理阶段,对程序中出现的所有 宏名
用相应的字符串进行代换,宏代换由预处理程序自动完成
a. 不带参数的宏定义
# define 标识符 字符串
字符串可以是常数,表达式,格式串等
约定:宏名一般用大写字母作为标识符,以便与变量名区别开,但用小写也没有语法错误
#include <stdio.h>
#define PI 3.14
int main (){
float S = PI * r * r;
printf("%f", S);
return 0;
}
若字符串中出现了某个宏名,不进行替换
#define PI 3.14
char *str = "PIE";//此时不会发生宏代换
宏代换只是简单的 字符串替换 ,不做语法检查。只有编译时才对源程序进行语法检查
宏定义的有效范围是从定义位置开始到文件结束。如果需要终止宏定义的作用域,可以用 #undef
#include <stdio.h>
#define PI 3.14
int main (){
#undef PI
float S = PI * r * r; //报错
printf("%f", S);
return 0;
}
宏定义之间可以相互引用
#define R 3.0
#define PI 3.14
#define C 2*PI*R
#define S PI*R*R
可以用宏定义表示数据类型
# define String char*
int main(){
String str = "The String";
return 0;
}
b. 带参数的宏定义
带参数的宏,在调用中,不仅要宏展开,而且要用实参数代替形参
#define 宏名(形参列表) 字符串
# define SUM(a,b) (a+b)
int main(){
int sum = SUM(1,2);
printf("%d\n",sum);
return 0;
}
-
宏标识符与形参列表之间不能有空格,否则会当被当做普通字符串
# define SUM (a,b) (a+b) int main(){ int sum = SUM(1,2);//会被替换成 (a,b) (a+b)(1,2) //编译不通过 printf("%d\n",sum); return 0; }
-
带参数的宏在宏代换时,只作简单的字符和参数的替换,不进行计算操作
-
宏定义时,要用
()
将形参字符串括住# define Twice(a) 2*a int main(){ int res = D(3+4);// 替换结果为:2*3+4 printf("%d\n",sum); return 0; }
# define Twice(a) 2*(a) int main(){ int res = D(3+4);// 替换结果为:2*(3+4) printf("%d\n",sum); return 0; }
-
宏定义时,要将计算结果也用
()
括住# define Pow(a) (a)*(a) int main(){ int res = Pow(10)/Pow(2); //宏代换后:10*10/2*2=100 return 0; }
# define Pow(a) ((a)*(a)) int main(){ int res = Pow(10)/Pow(2); //宏代换后:(10*10)/(2*2)=25 return 0; }
7.1.3 条件编译
程序中一部分代码在满足一定条件才进行编译,否则不参与编译
优点:按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件,有利于程序的移植和调试。生成的目标程序较短
#if 条件1
...
#elseif 条件2
...
#else
...
#endif
#define SCORE 67
#if SCORE > 90
printf("优秀\n");
code 优秀
#elif SCORE > 60
printf("良好\n");
code 良好
#else
printf("不及格\n");
code 不及格
#endif
7.2 别名typedef
typedef 原类型名 新类型名;
-
原类型名中含有定义部分,新类型名一般用大写表示
-
typedef
在编译时完成替换
7.2.1 typedef
的使用
a. 基本数据类型
typedef int Integer;
typedef Integer MyInteger;
int main(){
Integer a;//等价于int a
}
b. 数组
typedef int NAME[20];
NAME a;//等价于 int a[20];
c. 结构体类型
struct Person{
int age;
char *name;
};
typedef struct Person Person;
typedef struct Person{
int age;
char *name;
} Person;
typedef struct {
int age;
char *name;
} Person;
d. 枚举类型
enum Session{
Spring,
Summer,
Autumn,
Winter
};
typedef enum Session Session;
typedef enum Session {
Spring,
Summer,
Autumn,
Winter
}Session;
typedef enum {
Spring,
Summer,
Autumn,
Winter
}Session;
e. 指针
指向结构体的指针
typedef struct {
float x;
float y;
}Point;
typedef Point *PP;
指向函数的指针
int sum(int a, int b) {
int c = a + b;
printf("%d + %d = %d", a, b, c);
return c;
}
typedef int (*Fn)(int, int);
// 定义一个指向sum函数的指针变量p
Fn p = sum;
7.3 宏定义与函数、typedef区别
7.3.1 宏定义与函数的区别
带参数宏定义与函数形式类似
-
匹配问题:宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值
-
执行阶段:函数调用在程序运行时执行,而宏代换只在编译预处理阶段进行
-
执行效率:带参数的宏比函数具有更高的执行效率
7.3.2 宏定义与typedef
区别
宏定义与
typedef
都可对数据类型进行说明
- 宏定义只是简单的字符串替换,在预处理阶段完成
typedef
在编译时处理,是对类型说明符的重新命名
typedef char *String;//给char *起了别名String
int main(){
String str = "This is a String";
return 0;
}
#define String char * //用String对char *进行宏代换
int main(){
String str = "This is a String";
return 0;
}