二.程序的编译链接过程
2.1首先看一下预处理的指令
关于上面的预处理指令,大家可以自己测试一下
(测试例子)(注意是两行) ANSI 标准 C 定义的几个宏如下,大家可以简单的了解一下 __LINE__ 表示正在编译的文件的行号 __FILE__ 表示正在编译的文件的名字 __DATE__ 表示编译时刻的日期字符串,例如: "25 Dec 2007" __TIME__ 表示编译时刻的时间字符串,例如: "12:30:55" __STDC__ 判断该文件是不是定义成标准 C 程序
2.2宏定义 编译器在预处理的时候,就会把宏定义的数据替换成它的元身,这里我们必须了解什么是宏,宏的主要的用法 2.2.1 定义宏常量 #define PI 3.14159265358979323846264338327 比如我们经常使用的一个变量,如果这个变量的数值改变了之后,我们不需要再文件里面一个一个找,而是只需要把宏改变就可以了 #define ERROR_TEST -1 比如我们在test文件中,一些代码的地方需要使用return -1,或者是exit(-1) 然后有很多类型的错误返回,如果每次返回的是-1,-2,-3之类的,一会就把自己给绕晕了 2.2.2定义字符串常量 #define PATH "c/file/linux" 这个一般用在表示文件的路径的情况下有很多 (大家下去看一下,如果是一行写不下怎么办呢,就是比如一行太长了怎么办) 2.2.3 定义注释 #define ZZ // 因为在程序编译的时候,注释是先于宏处理的 2.2.4 定义表达式(宏函数) #define MUL(x) x*x 纯文本替换 测试用例 MUL(5);MUL(1+2); --- 加上括号MUL((x)*(x)) MUL(5*3)*MUL(5*3) --- 大括号???不要吝啬括号 2.2.5 空格 #define MUL (x) ((x)*(x)) 比如上面的宏函数,我们要是定义成下面的这个样子又是什么样的呢 2.2.6#undef 撤销宏定义 比如 #define PI 3.14159265358979323846264338327 ... ... ... #undef PI此时PI不在有效了 2.2.7定义宏函数补充 比如下面的程序 #define PRINTF printf("测试");
这个时候我们可以编写 一个if esle语句 if(0) PRINTF; else printf("haha\n");
宏函数比普通函数的一些特性在哪里 (1)普通的函数需要建立栈帧,开销较大,宏函数在效率上面更胜一筹 (2)函数中的参数必须使用特定的类型,换句话说,宏函数对参数是不进行类型检查的,宏函数是不类型限制的
宏函数的缺点 宏函数每次是进行的代码的替换,有时候可能会增加代码的长度,使维护起来非常的复杂 宏函数的参数不能是一个类型
2.3. 条件编译 第一种形式: #ifdef 标识符 程序段 1 #else 程序段 2 #endif 它的功能是,如果标识符已被 #define 命令定义过则对程序段 1 进行编译;否则对程序段 2 进行编译。
第二种形式 #ifndef 标识符程序段 1 #else 程序段 2 #endif
第三种形式 #if 常量表达式 程序段 1 #else 程序段 2 #endif
2.4. 文件包含 文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。C语言提供#include 命令来实现文件包含的操作,它实际是宏替换的延伸,有两种格式: #include<filename> #include"filename" 2.5.#pragma 预处理 自己上网调研,写博客,写测试用例 #pragma once #pragma warning #pragma pack--这个是张晨亮学长给你们讲过的,设置内置内存对齐数
2.6详解程序编译和链接过程 程序的编译和链接的过程 我们在linux下使用gcc编译器直接对程序进行编译,比如使用gcc test.c,然后就会生成一个程序叫做test.out 但是上面的看似简单的过程实际上是经历了四个阶段 预处理,编译,汇编,连接 每个过程都有自己要完成的任务
记住一些操作,ESc对应的是iso,
2.6.1. 预处理 通过预处理生成一个.i的文件,使用的指令是 gcc -E test.c -o test.i (G) 预处理过程主要处理的是1. 以#开始的预处理指令(保留#pragma)2.删除所有的注释3.添加文件名和行号,具体的过程如下
- 将所有的#define删除,展开所有的宏定义
- 处理所有的条件预编译指令,比如#if,#ifdef....
- 处理所有的#include预编译指令,将所有被包含的文件包含至本文件中来
- 删除所有的注释,//和/* */等
- 保留所有的#pragma预处理指令,因为下面还要用到它
推荐书目:《程序员的自我修养--链接、装载与库》 《C语言深度剖析》