编译预处理
程序编译的四个步骤

程序编译的四个步骤程序编译通常涉及以下四个步骤:预处理、编译、汇编和链接。
1.预处理预处理是编译过程的第一步,它主要负责对源代码进行一些预处理操作。
预处理器工具通常被称为预处理程序,它会根据源代码文件中的预处理指令来修改源代码。
预处理指令位于源代码文件的开头,以“#”字符开头。
预处理指令主要包括宏定义、条件编译和包含文件等。
在预处理阶段,预处理器会执行以下操作:-展开宏定义:将代码中的宏定义替换为相应的代码片段。
-处理条件编译:根据条件编译指令的结果,决定是否包含或排除一些代码。
-处理包含文件:将文件中的包含文件指令替换为实际的文件内容。
预处理后的源代码通常会生成一个中间文件,供下一步编译使用。
2.编译编译是程序编译过程的第二个阶段。
在编译阶段,编译器将预处理生成的中间文件翻译成汇编语言。
编译器会按照源代码的语法规则,将源代码转换为汇编语言指令,生成目标文件(也称为汇编代码文件)。
编译器在编译过程中执行以下操作:-词法分析:将源代码分割为多个词法单元,如关键字、标识符和运算符等。
-语法分析:根据语言的语法规则,分析词法单元的组合,生成语法树。
-语义分析:检查语法树的语义正确性,进行类型检查等。
-优化:对生成的中间代码进行各种优化,以提高程序执行效率。
编译器输出的目标文件通常是汇编语言形式的代码,以便下一步汇编使用。
3.汇编汇编是编译过程的第三个阶段,它将编译器生成的汇编代码翻译成目标机器码。
汇编器(或称为汇编程序)将汇编代码中的指令和操作数翻译为目标机器指令的二进制表示。
汇编器在汇编过程中执行以下操作:-识别和解析汇编指令:将汇编代码中的汇编指令和操作数分割解析。
-确定存储器地址:根据符号的引用和定义,计算并分配存储器地址。
-生成目标机器指令:将汇编指令和操作数翻译为目标机器指令的二进制表示。
汇编器的输出是一个或多个目标文件,每个目标文件都包含可在目标机器上执行的二进制指令。
4.链接链接是编译的最后一个阶段,它将多个目标文件和库文件组合在一起,生成最终的可执行文件。
第10章 预处理过程

20
考虑圆括号对宏的影响: #define squ (x) x*x #define sqa (x) (x)* x #define sq (x) ((x)* (x)) //这个宏实现求表达式平方的功能 int k=squ(1+2)/squ(2+1); //宏展开为: int k=1+2*1+2/2+1*2+1; //相当于 int k=7; int m=sqa(1+2)/sqa(2+1); //宏展开为: int m=(1+2)*1+2/(2+1)*2+1; // 相当于 int m=4; int n=sq(1+2)/sq(2+1); // 展开为: int n=((1+2)*(1+2))/((2+1)*(2+1)); // 相当于 int n=1;
9
[例] #define引入CALLBACK作为占位符,标识符 CALLBACK处理为一个空白。
#include< iostream.h> #define CALLBACK CALLBACK void f (char* s) { cout<<s<<endl; } void main () //表示f当作回调函数, { void ( *pf )( char* )=&f; ( *pf ) ("CALLBACK funct"); //输出: CALLBACK funct } //在字符串中的宏名CALLBACK不被替换
4
续行符\即反斜杠\之后紧跟换行符(硬回车)结束的行 与下一行合并为单独的一行,这要求反斜杠\与换行符之间 没有空格即反斜杠\之后紧跟换行符(硬回车),标记为\, 不能写为\ 。 硬回车实际上是不可见的。可以用续行符\来处理一 行容纳不下的宏替换指令。 例如: #define LONGTEXT abc*d[ i ] \ +b[ i ]/c[ j ]- d[ k ] 合并为内在的逻辑行: #define LONGTEXT abc*d[i]+b[i]/c[j]-d[k] 预处理指令可以分布在源程序的任何位置,但应与程序 源代码控制流程分隔开来。非空的源程序结束于前面没有反 斜杠的换行符。
编译预处理

编译预处理1概述:编译预处理是在源程序正式编译前的处理。
预处理名令一般写在程序的最开头,并且以#开头的命令。
编译预处理命令不是c语言本身的组成部分,也不属于c语句,不能直接对他们编译。
在代码的正式编译之前(编译即指转换成二进制的机器语言),系统先对预处理命令进行处理,然后再由编译程序对处理后的程序进行正常的编译,得到可执行文件。
即对一个源程序进行编译时,系统会先引用预处理命令对源程序中的预处理部分进行处理,然后自动进行源程序的编译。
C语言提供3中预处理命令:宏替换文件包含条件编译他们均以#开头,并独占一个书写行,语句结尾不用;作为结束符。
2 宏替换(宏定义)分为两种:(1)无参数的宏替换是指用一个指定的标识符(即宏名)来代表程序中的一个字符串。
格式#define 宏名字符串如#define SIZE 10SIZE为宏名,此命令执行后,预处理程序对源程序中的所有SIZE的标识符用10替换。
说明:①宏名一般用大写字符,但不是必须的。
②字符串可以是常量,表达式,语句或多条语句可以是任何语句如输出语句,赋值语句等等③宏定义与变量定义不同,只是做字符的简单替换,不占内存空间,也不赋值④结尾不能加;,如果加了;,则;也作为字符串的一部分,一同参与替换。
⑤宏定义允许嵌套定义,即在宏定义的字符串中可以使用已经定义的宏名。
⑥宏定义要写在函数之外的,一般写在程序的开头,作用范围是从定义到本文件结束,出来这个文件失去作用了。
若要终止其作用,在需要终止前面加#undef 宏名⑦若宏名出现在双引号中,则将不会发生宏替换。
如printf(“ADD”) ADD是宏,这里不会进行宏替换了⑧替换文本不替换用户标识符中的成分宏名ADD不会替换标识符ADDIP中的ADD(2)有参数的宏替换宏定义中的参数为形式参数,在宏调用中的参数为实际参数。
格式:#define 宏名(形参)字符串各参数间用,隔开。
替换时,不仅要将宏展开,还要将形参替换为实参,但是仅仅是替换而不会去运算得出一个值,这点千万注意。
预编译处理

预编译处理【学习目标】◇理解编译预处理的概念。
◇了解宏定义的概念,掌握简单宏定义和带参数的宏定义的格式和使用方法。
◇了解文件包含的概念,掌握文件包含的格式和使用方法。
能在程序中合理使用#include预处理指令◇了解条件编译的概念,掌握条件编译的三种格式及其使用方法。
能在程序中合理使用#define, #if, #ifndef, #else, #undef, #elif等指令。
【重点和难点】重点:编译预处理的概念,简单的宏定义与文件包含指令的用法。
难点:带参宏定义,条件编译指令,会用条件指令解决文件的重复包含问题。
【学习方法指导】本章的内容比较简单,严格说来,它也不算是C++语言的组成部分。
但是,一般说来,任何程序都离不开预编译指令。
特别是文件包含指令和条件编译指令,应把它们搞清楚。
虽然可以用宏定义的方法定义常数,但推荐使用const语句定义常量。
在编程中,如果我们能恰当地运用条件编译,就可以提高程序运行的效率。
【知识点】宏定义;宏替换;简单的宏定义;带参数的宏定义;文件包含;条件编译第一节宏定义我们用C++进行编程的时候,可以在源程序中包括一些编译命令,以告诉编译器对源程序如何进行编译。
这些命令包括:宏定义、文件包含和条件编译,由于这些命令是在程序编译的时候被执行的,也就是说,在源程序编译以前,先处理这些编译命令,所以,我们也把它们称之为编译预处理,本章将对这方面的内容加以介绍。
实际上,编译预处理命令不能算是C++语言的一部分,但它扩展了C++程序设计的能力,合理地使用编译预处理功能,可以使得编写的程序便于阅读、修改、移植和调试。
预处理命令共同的语法规则如下:◇所有的预处理命令在程序中都是以"#"来引导如"#include "stdio.h""。
◇每一条预处理命令必须单独占用一行,如"#include "stdio.h" #include <stdlib.h>" 是不允许的。
程序编译的四个阶段

程序编译的四个阶段
四个阶段分别是: 预处理,编译,组装,链接
1. 预处理将头⽂件展开,将宏定义替换,⽣成符号⽂件.S
2. 编译则包含了词法检查,语法检查,权限检查, 代码优化
3. 组装:将编译后的代码组装成机器码,形成位置⽆关的⽬标⽂件 .o
4. 链接将多个位置⽆关的⽬标⽂件合并成可执⾏⽂件
可见组装才是平台相关的,之前的操作都与平台⽆关,换句话说是编译前端和编译后端
具体有个例⼦
⼀个类的成员变量修改了访问控制符,在另外⼀个⽂件被引⽤,是否必须编译修改的⽂件才能链接成功?答案是不需要
例如我们有 abc.hpp abc.cpp 定义了⼀个class
class a {
public:
int a = 0;
};
在main.cpp 中有引⽤
int main(){
a a;
std::cout << a.a;
}
这样是可以编译成功
# ⽣成main.o abc.o
g++ -c main.cpp abc.cpp
# 链接
g++ -o main main.o abc.o
# 成功
然后修改public为private 重新编译abc
g++ -c abc.cpp
# 重新链接
g++ -o main main.o abc.o
#成功!且可以执⾏
但是重新编译main.cpp
g++ -c main.cpp
#失败,提⽰⽆法访问private成员
可见,访问权限是在编译期检查的,编译成⽬标⽂件后,就不会去检查了
总结
编译成⽬标⽂件或者库⽂件后,不会再去检查权限位了,运⾏时照样可以访问。
C语言程序设计 第3版 第9章 编译预处理

#include "test2.c" static int sum(int n) {
int i,s=0; for(i=1;i<=n;i++)
s=s+fact(i); return s; }
static int fact(int n) {
C语言程序设计
第9章 编译预处理
第1讲:编译预处理基本形式
提纲
1.宏定义 2.文件包含 3.条件编译
1.宏定义
不带参数宏定义 带参数宏定义
格式:
#define 标识符 字符串
功能:
指定标识符代替一个较复杂的字符串。
注意说明:
(1)宏名一般习惯用大写字母,例如宏名PI。 (2)宏名用做代替一个字符串,不作语法检查。 (3)宏定义无需在末尾加“;” (4)宏定义的有效范围为#undef命令终止。 (5)在进行宏定义时,可以引用已定义的宏名。
char web[50]; int i=0; gets(web); while(web[i]!='\0') {
#if(R==1) if(web[i]>='A'&&web[i]<='Z') {web[i]=web[i]+32; i++;}
#else if(web[i]>='a'&&web[i]<='z') {web[i]=web[i]-32; i++;}
形式3:
#ifndef 标识符 程序段1
#else 程序段2
第八章编译预处理

《C语言程序设计》 数组
文件包含示例: aaa.c文件
#include “bbb.c”
bbb.c文件 int max(int x,int y) பைடு நூலகம் if(x>y) return x; else return y; }
输出结果: 0 1 1 0
《C语言程序设计》 数组
8.2 文件包含
文件包含是指一个文件可以把其它文件的内容包 含进来,其一般格式为: 首先在当前目 格式1: #include “文件名” 录中查找被包 含的文件,找 格式2: #include <文件名> 不到时再按系 统指定的标准 仅在系统指定的标准目录 目录中查找。 中查找被包含的文件。
#ifdef 标识符 程序段1 [#else 程序段2] #endif #ifndef 标识符 程序段1 [#else 程序段2] #endif #if 表达式 程序段1 [#else 程序段2] #endif
#undef 标识符
[例8.5] 条件编译
《C语言程序设计》 数组
第八章作业
8.1 下列程序意在打印出22、32和42。请根据程序的 运行结果分析该程序是否正确。请把宏改写为函数, 再进行分析。 #define SQR(a) ((a)*(a)) main() { int i,x=2; for(i=1;i<=3;i++) printf(“%d “,SQR(x++)); } 8.2 分别用宏和函数完成,从两个数中找出最大数。
C语言文件的编译到执行的四个阶段

C语言文件的编译到执行的四个阶段C语言程序的编译到执行过程可以分为四个主要阶段:预处理、编译、汇编和链接。
1.预处理:在这个阶段,编译器会执行预处理指令,将源代码中的宏定义、条件编译和包含其他文件等操作进行处理。
预处理器会根据源代码中的宏定义替换相应的标识符,并去除注释。
预处理器还会将包含的其他文件插入到主文件中,并递归处理这些文件。
处理后的代码被称为预处理后的代码。
2.编译:在这个阶段,编译器将预处理后的代码转换成汇编代码。
汇编代码是一种低级的代码,使用符号来表示机器指令。
编译器会对源代码进行词法分析、语法分析和语义分析,生成相应的中间代码。
中间代码是一种与特定硬件无关的代码表示形式,便于后续阶段的处理。
3.汇编:在这个阶段,汇编器将中间代码转化为机器可以执行的指令。
汇编器会将汇编代码翻译成二进制形式的机器指令,并生成一个目标文件。
目标文件包含了机器指令的二进制表示以及相关的符号信息。
4.链接:在C语言中,程序通常由多个源文件组成,每个源文件都经过了预处理、编译和汇编阶段得到目标文件。
链接器的作用就是将这些目标文件合并成一个可执行文件。
链接器会解析目标文件中的符号引用,找到其对应的定义并进行连接。
链接器还会处理库文件,将使用到的函数和变量的定义从库文件中提取出来并添加到目标文件中。
最终,链接器生成一个可以直接执行的可执行文件。
以上是C语言程序从编译到执行的四个阶段。
每个阶段都有特定的任务,并负责不同层次的代码转换和处理。
通过这四个阶段,C语言程序可以从源代码转换为机器能够执行的指令,并最终被计算机执行。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第九章编译预处理9.1 选择题【题9.1】以下叙述中不正确的是。
A)预处理命令行都必须以#号开始B)在程序中凡是以#号开始的语句行都是预处理命令行C)C程序在执行过程中对预处理命令行进行处理D)以下是正确的宏定义#define IBM_PC【题9.2】以下叙述中正确的是。
A)在程序的一行上可以出现多个有效的预处理命令行B)使用带参的宏时,参数的类型应与宏定义时的一致C)宏替换不占用运行时间,只占编译时间D)在以下定义中C R是称为“宏名”的标识符#define C R 045【题9.3】请读程序:#define ADD(x) x+xmain(){int m=1,n=2,k=3;int sum=ADD(m+n)*k;printf(“sum=%d”,sum);}上面程序的运行结果是。
A)sum=9 B)sum=10 C)sum=12 D)sum=18【题9.4】以下程序的运行结果是。
#define MIN(x,y) (x)<(y)?(x):(y)main(){int i=10,j=15,k;k=10*MIN(i,j);printf(“%d\n”,k);}A)10 B)15 C)100 D)150【题9.5】在宏定义#define PI 3.14159中,用宏名PI代替一个。
A)常量B)单精度数C)双精度数D)字符串【题9.6】以下程序的运行结果是。
#include <stdio.h>#define FUDGE(y) 2.84+y#define PR(a) printf(“%d”,(int)(a))#define PRINT1(a) PR(a); putchar(‘\n’)main(){int x=2;PRINT1(FUDGE(5)*x);}A)11 B)12 C)13 D)15【题9.7】以下有关宏替换的叙述不正确的是。
A)宏替换不占用运行时间B)宏名无类型C)宏替换只是字符替换D)宏名必须用大写字母表示【题9.8】C语言的编译系统对宏命令的处理是。
A)在程序运行时进行的B)在程序连接时进行的C)和C程序中的其它语句同时进行编译的D)在对源程序中其它成份正式编译之前进行的【题9.9】若有宏定义如下:#define X 5#define Y X+1#define Z Y*X/2则执行以下printf语句后,输出结果是。
int a; a=Y;printf(“%d\n”,Z);printf(“%d\n”,--a);A)7 B)12 C)12 D)76 6 5 5【题9.10】若有以下宏定义如下:#define N 2#define Y(n) ((N+1)*n)则执行语句z=2*(N+Y(5));后的结果是。
A)语句有错误B)z=34 C)z=70 D)z无定值【题9.11】若有宏定义:#define MOD(x,y) x%y则执行以下语句后的输出为。
int z,a=15,b=100;z=MOD(b,a);printf(“%d\n”,z++);A)11 B)10 C)6 D)宏定义不合法【题9.12】以下程序的运行结果是。
#define MAX(A,B) (A)>(B)?(A):(B)#define PRINT(Y) printf(“Y=%d\t”,Y)main(){int a=1,b=2,c=3,d=4,t;t=MAX(a+b,c+d);PRINT(t);}A)Y=3 B)存在语法错误C)Y=7 D)Y=0【题9.13】以下程序段中存在错误的是。
A)#define array_size 100int array1[array_size];B)#define PI 3.14159#define S(r) PI*(r)*(r)…area=S(3.2);C)#define PI 3.14159#define S(r) PI*(r)*(r)…area=S(a+b);D)#define PI 3.14159#define S(r) PI*(r)*(r)…area=S(a);【题9.14】请读程序:#include <stdio.h>#define MUL(x,y) (x)*ymain(){int a=3,b=4,c;c=MUL(a++,b++);printf(“%d\n”,c);}上面程序的输出结果是。
A)12 B)15 C)20 D)16【题9.15】#define能作简单的替代,用宏替代计算多项式4*x*x+3*x+2之值的函数f,正确的宏定义是。
A)#define f(x) 4*x*x+3*x+2B)#define f 4*x*x+3*x+2C)#define f(a) (4*a*a+3*a+2)D)#define (4*a*a+3*a+2) f(a)【题9.16】对下面程序段:#define A 3#define B(a) ((A+1)*a)…x=3*(A+B(7));正确的判断是。
A)程序错误,不许嵌套宏定义B)x=93C)x=21D)程序错误,宏定义不许有参数【题9.17】以下程序中,第一个输出值是(1),第二个输出值是(2)。
#include <stdio.h>#define M 3#define N (M+1)#define NN N*N/2main(){printf(“%d\n”,NN);printf(“%d”,5*NN);}【1】A)3 B)4 C)6 D)8【2】A)17 B)18 C)30 D)40【题9.18】以下程序的输出结果为。
#include <stdio.h>#define F(y) 3.84+y#define PR(a) printf(“%d”,(int)(a))#define PRINT(a) PR(a); putchar(‘\n’)main(){int x=2;PRINT(F(3)*x);}A)8 B)9 C)10 D)11【题9.19】以下程序的输出结果为。
#define PT 5.5#define S(x) PT*x*xmain(){int a=1,b=2;printf(“%4.1f\n”,S(a+b));}A)12.0 B)9.5 C)12.5 D)33.5【题9.20】以下在任何情况下计算平方数时都不会引起二义性的宏定义是。
A)#define POWER(x) x*xB)#define POWER(x) (x)*(x)C)#define POWER(x) (x*x)D)#define POWER(x) ((x)*(x))【题9.21】在“文件包含”预处理语句的使用形式中,当#include后面的文件名用“”(双引号)括起时,寻找被包含文件的方式是。
A)直接按系统设定的标准方式搜索目录B)先在源程序所在目录搜索,再按系统设定的标准方式搜索C)仅仅搜索源程序所在目录D)仅仅搜索当前目录【题9.22】在“文件包含”预处理语句的使用形式中,当#include后面的文件名用< >(尖括号)括起时,寻找被包含文件的方式是。
A)仅仅搜索当前目录B)仅仅搜索源程序所在目录C)直接按系统设定的标准方式搜索目录D)先在源程序所在目录搜索,再按系统设定的标准方式搜索【题9.23】请读程序:#define LETTER 0main(){char str[20]=“C Language”,c;int i;i=0;while((c=str[i])!=‘\0’){i++;#if LETTERif(c>=‘a’&&c<=‘z’)c=c-32;#elseif(c>=‘A’&&c<=‘Z’)c=c+32;#endifprintf(“%c”,c);}}上面程序的运行结果是。
A)C Language B)c language C)C LANGUAGE D)c lANGUAGE【题9.24】以下正确的描述是。
A)C语言的预处理功能是指完成宏替换和包含文件的调用B)预处理指令只能位于C源程序文件的首部C)凡是C源程序中行首以“#”标识的控制行都是预处理指令D)C语言的编译预处理就是对源程序进行初步的语法检查【题9.25】C语言提供的预处理功能包括条件编译,其基本形式为:#XXX 标记符程序段1#else程序段2#endif这里XXX可以是。
A)define或include B)ifdef或includeC)ifdef或ifndef或define D)ifdef或ifndef或if9.2 填空题【题9.26】设有以下宏定义:#define WIDTH 80#define LENGTH WIDTH+40则执行赋值语句:v=LENGTH*20; (v为int型变量)后,v的值是。
【题9.27】设有以下宏定义:#define WIDTH 80#define LENGTH (WIDTH+40)则执行赋值语句:k=LENGTH*20; (k为int型变量)后,k的值是。
【题9.28】下面程序的运行结果是。
#define DOUBLE(r) r*rmain(){int x=1,y=2,t;t=DOUBLE(x+y);pri ntf(“%d\n”,t);}【题9.29】下面程序的运行结果是。
#define MUL(z) (z)*(z)main()printf(“%d\n”,MUL(1+2)+3);}【题9.30】下面程序的运行结果是。
#define POWER(x) ((x)*(x))main(){int i=1;while(i<=4) printf(“%d\t”,POWER(i++));printf(“\n”);}【题9.31】下面程序的运行结果是。
#define EXCH(a,b) { int t; t=a; a=b; b=t; }main(){int x=5,y=9;EXCH(x,y);printf(“x=%d,y=%d\n”,x,y);}【题9.32】下面程序的运行结果是。
#define MAX(a,b,c) ((a)>(b)?((a)>(c)?(a):(c)):((b)>(c)?(b):(c)))main(){int x,y,z;x=1; y=2; z=3;printf(“%d,”,MAX(x,y,z));printf(“%d,”,MAX(x+y,y,y+x));printf(“%d\n”,MAX(x,y+z,z));}【题9.33】下面程序的运行结果是。
#define SELECT(a,b) a<b?a:bmain(){int m=2,n=4;printf(“%d\n”,SELECT(m,n));}【题9.34】下面程序的运行结果是。