导航:首页 > 源码编译 > 在源程序被编译器处理之前

在源程序被编译器处理之前

发布时间:2022-12-10 09:17:26

‘壹’ C语言源程序到运行程序经过哪几个步骤

1、预处理

在这一阶段,源码中的所有预处理语句得到处理,例如:#include语句所包含的文件内容替换掉语句本身,所有已定义的宏被展开。

根据#ifdef,#if等语句的条件是否成立取舍相应的部分,预处理之后源码中不再包含任何预处理语句。

GCC预处理阶段可以生成.i的文件,通过选项-E可以使编译器在预处理结束时就停止编译。例如:gcc -E -o hello.i hello.c

2、编译

这一阶段,编译器对源码进行词法分析、语法分析、优化等操作,最后生成汇编代码。这是整个过程中最重要的一步,因此也常把整个过程称为编译。

可以通过选项-S使GCC在进行完编译后停止,生成.s的汇编程序。例如:gcc -S -o hello.s hello.c

3、汇编

这一阶段使用汇编器对汇编代码进行处理,生成机器语言代码,保存在后缀为.o的目标文件中。

当程序由多个代码文件构成时,每个文件都要先完成汇编工作,生成.o目标文件后,才能进入下一步的链接工作。

目标文件已经是最终程序的某一部分了,只是在链接之前还不能执行。可以通过-c选项生成目标文件:gcc -c -o hello.o hello.c

4、链接

经过汇编以后的机器代码还不能直接运行。为了使操作系统能够正确加载可执行文件,文件中必须包含固定格式的信息头,还必须与系统提供的启动代码链接起来才能正常运行,这些工作都是由链接器来完成的。gcc -o hello hello.c

5、运行:执行.EXE文件,得到运行结果。

‘贰’ java的源程序是怎样被编译和运行的

说得简单易懂一点,就是JAVA的源程序通过其编译器编译成JVM能够能够读懂的源码,然后由JVM来负责执行,所以简单说编译器可以理解成为一个解码的作用。JVM才是真正的执行工具。JVM可以跨平台,但是运行JAVA无论什么平台都必须有JVM的支持。

‘叁’ c语言预处理

其实网络文库也讲得挺明白的,你可以打开一个.h的头文件看看里面,对应这三点,就很清楚了。一.宏定义1.不带参数的宏定义: 宏定义又称为宏代换、宏替换,简称“宏”。 格式: #define 标识符 字符串 其中的标识符就是所谓的符号常量,也称为“宏名”。 预处理(预编译)工作也叫做宏展开:将宏名替换为字符串。 掌握"宏"概念的关键是“换”。一切以换为前提、做任何事情之前先要换,准确理解之前就要“换”。 即在对相关命令或语句的含义和功能作具体分析之前就要换: 例: #define PI 3.1415926 把程序中出现的3.1415926全部换成PI 说明: (1)宏名一般用大写 (2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义 (3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。 (4)宏定义末尾不加分号; (5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。 (6)可以用#undef命令终止宏定义的作用域 (7)宏定义可以嵌套 (8)字符串" "中永远不包含宏 (9)宏定义不分配内存,变量定义分配内存。 2.带参数的宏: 除了一般的字符串替换,还要做参数代换 格式: #define 宏名(参数表) 字符串 例如:#define S(a,b) a*b area=S(3,2);第一步被换为area=a*b; ,第二步被换为area=3*2; 类似于函数调用,有一个哑实结合的过程: (1)实参如果是表达式容易出问题 #define S(r) r*r area=S(a+b);第一步换为area=r*r;,第二步被换为area=a+b*a+b; 正确的宏定义是#define S(r) (r)*(r) (2)宏名和参数的括号间不能有空格 (3)宏替换只作替换,不做计算,不做表达式求解 (4)函数调用在编译后程序运行时进行,并且分配内存。宏替换在编译前进行,不分配内存 (5)宏的哑实结合不存在类型,也没有类型转换。 (6)函数只有一个返回值,利用宏则可以设法得到多个值 (7)宏展开使源程序变长,函数调用不会 (8)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值) 编辑本段二. 文件包含一个文件包含另一个文件的内容 格式: #include "文件名" 或 #include <文件名> 编译时以包含处理以后的文件为编译单位,被包含的文件是源文件的一部分。 编译以后只得到一个目标文件.obj 被包含的文件又被称为“标题文件”或“头部文件”、“头文件”,并且常用.h作扩展名。 修改头文件后所有包含该文件的文件都要重新编译 头文件的内容除了函数原型和宏定义外,还可以有结构体定义,全局变量定义: (1)一个#include命令指定一个头文件; (2)文件1包含文件2,文件2用到文件3,则文件3的包含命令#include应放在文件1的头部第一行; (3)包含可以嵌套; (4)<文件名>称为标准方式,系统到头文件目录查找文件, "文件名"则先在当前目录查找,而后到头文件目录查找; (5)被包含文件中的静态全局变量不用在包含文件中声明。 编辑本段三. 条件编译有些语句行希望在条件满足时才编译。 格式:(1) #ifdef 标识符 程序段1 #else 程序段2 #endif 或 #ifdef 程序段1 #endif 当标识符已经定义时,程序段1才参加编译。 格式:(2) #ifndef 标识符 格式:(3) #if 表达式1 程序段1 #else 程序段2 #endif 当表达式1成立时,编译程序段1,当不成立时,编译程序段2。 使用条件编译可以使目标程序变小,运行时间变短。 预编译使问题或算法的解决方案增多,有助于我们选择合适的解决方案。 此外,还有布局控制:#pragma,这也是我们应用预处理的一个重要方面,主要功能是为编译程序提供非常规的控制流信息。

‘肆’ C语言的编译系统对宏命令的处理

前言:宏实质就是编译器在对代码进行编译之前进行的一个“查找替换”工作,就跟你在处理文档时用WPS/WORD/记事本等进行“查找替换”操作一样。

C语言的编译系统对宏命令的处理是()
A。在程序运行时进行的
B。在对源程序中其他成分正式编译之前进行的
C。在程序连续时进行的
D。和C程序中的其他语句同时进行编译

答:选B。在对源程序中其他成分正式编译之前进行的

2,
#define N 2 /* 在预编译时将用下面代码中N替换成2 */
#define M N+1 /* 在预编译时将M替换成N+1(即2+1,N会再被替换成2)*/
#define NUM 2*M+1 /* 如上:在预编译时NUM替换成2*M+1,即2*N+1+1,即2*2+1+1*/
main()
{int i;
for(i=1;i<=NUM;i++)printf("%d\n",i);
/*
* 如上所述,上句展开为:for(i=1;i<=2*2+1+1;i++)printf("%d\n",i);
* 所以:循环将执行6次
*/
}

/* 切记注意:每一个宏百进行替换时只是替换“正文”中的内容,而不包括预编译语句的内容, 否则就会像不少人理解的那样,错误地认为第2个题中的循环将执行7次 */

‘伍’ 编译程序预处理干什么

编译预处理是C语言区别于其它高级程序设计语言的特征之一,它属于C语言编译系统的一部分。C程序中使用的编译预处理命令均以#开头,它在C编译系统对源程序进行编译之前,先对程序中这些命令进行“预处理”。编译预处理命令的三种不同形式:宏定义、文件包含和条件编译。

‘陆’ C语言文件的编译与执行的四个阶段并分别描述

开发C程序有四个步骤:编辑、编译、连接和运行。

任何一个体系结构处理器上都可以使用C语言程序,只要该体系结构处理器有相应的C语言编译器和库,那么C源代码就可以编译并连接到目标二进制文件上运行。

1、预处理:导入源程序并保存(C文件)。

2、编译:将源程序转换为目标文件(Obj文件)。

3、链接:将目标文件生成为可执行文件(EXE文件)。

4、运行:执行,获取运行结果的EXE文件。

(6)在源程序被编译器处理之前扩展阅读:

将C语言代码分为程序的几个阶段:

1、首先,源代码文件测试。以及相关的头文件,比如stdio。H、由预处理器CPP预处理为.I文件。预编译的。文件不包含任何宏定义,因为所有宏都已展开,并且包含的文件已插入。我归档。

2、编译过程是对预处理文件进行词法分析、语法分析、语义分析和优化,生成相应的汇编代码文件。这个过程往往是整个程序的核心部分,也是最复杂的部分之一。

3、汇编程序不直接输出可执行文件,而是输出目标文件。汇编程序可以调用LD来生成可以运行的可执行程序。也就是说,您需要链接大量的文件才能获得“a.out”,即最终的可执行文件。

4、在链接过程中,需要重新调整其他目标文件中定义的函数调用指令,而其他目标文件中定义的变量也存在同样的问题。

‘柒’ c语言源程序是有什么组成的

所有编程语言本质上都是由算法+数据结构组成的。

一个C语言源程序,是由一个或多个函数定义顺序组成的,其中必须有一个函数名为main的函数,main()函数又称为主函数。C语言源程序的次要构成成分有:编译预处理命令、注释和声明。

主函数被编译程序翻译成一个机器语言形式的主程序段,任何其他函数都将被编译程序翻译成机器语言形式的子程序段。

换言之,C语言源程序中的函数,并非数学中时常显得有些高深莫测的函数,它只是完成特定数据处理任务的、功能上独立的一个程序段而已。

(7)在源程序被编译器处理之前扩展阅读:

C语言特有特点

1、C语言是一个有结构化程序设计、具有变量作用域(variable scope)以及递归功能的过程式语言。

2、C语言传递参数均是以值传递(pass by value),另外也可以传递指针(a pointer passed by value)。

3、不同的变量类型可以用结构体(struct)组合在一起。

4、只有32个保留字(reserved keywords),使变量、函数命名有更多弹性。

5、部份的变量类型可以转换,例如整型和字符型变量。

6、通过指针(pointer),C语言可以容易的对存储器进行低级控制。

7、预编译处理(preprocessor)让C语言的编译更具有弹性。

参考资料:网络-C语言

‘捌’ 计算机程序设计语言分为四类

计算机程序设计语言分为四类

为了让计算机解决实际问题,人们从一开始就不断地开展程序设计工作,这里的“程序”就是计算机能够执行的指令代码(机器码和其它代码)。程序设计人员还必须在一个被称为“计算机程序设计语言(也可以称为编译或解释性语言)”的环境中开展编程。

计算机程序设计语言

是指程序设计人员和计算机都可以识别的程序代码(包括0和1机器代码)规则,是人与计算机进行交流的工具,可以把程序设计语言分为以下四类。

1.机器语言

机器语言是一种CPU指令系统, 被称为CPU的机器语言, 它是CPU可以识别的一组由0和1序列构成的指令码。用机器语言编程序, 就是从所使用的CPU的指令系统中挑选合适的指令,组成一个指令序列。这种程序可以被机器直接理解并执行,速度很快,但由于不直观、难记、难以理解、不易查错、开发周期长,很难推广应用下去,因此,只有专业人员在编制对于执行速度有很高要求的程序时才采用这种代码。

2.汇编语言

为了减轻编程者的劳动强度,人们使用一些用于帮助记忆的符号来代替机器语言中的0、1机器指令代码序列,使得编程效率和质量得到极大的提高。把这些助记符组成的指令系统称为汇编语言。汇编语言是指令与机器语言指令基本上是一一对应的。由于这些助记符号不能被机器直接识别,所以汇编语言代码程序必须被编译成机器语言程序才能被机器理解和执行。编译之前的程序被称为“源程序”,编译之后的被称为“目标程序”。

汇编语言与机器语言都是因CPU的不同而不同, 所以统称为“面向机器的语言”。使用这类语言,可以编出效率极高的程序,但对程序设计人员的要求也很高。他们不仅要考虑解题思路,还要熟悉机器的内部结构,一般的人很难掌握这类程序设计语言,还是不能大范围推广应用。

3.面向过程的语言

面向过程思想是一种以过程为中心的编程思想,是以什么正在发生为主要目标进行编程。面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

把解题的过程看做是数据被加工的过程,这种程序设计语言称为面向过程的程序设计语言。常用的面向过程的语言有C、Fortran、Basic、Pascal等。使用这类编程语言,程序设计者可以不关心机器的内部结构甚至工作原理,把主要精力集中在解决问题的思路和方法上。这类摆脱了硬件束缚的程序设计语言被统称为高级语言。高级语言的出现大大地提高了编程效率,使人们能够开发出越来越大、功能越来越强的程序。要运行使用面向过程语言编制的程序,一般有两种方法:(1)解释型,(2)编译型。

解释型语言在程序编制完成之后,按照程序编排的顺序一条条地把指令语句转换为机器代码然后执行。因为每次运行中每条语句都要进行转换和执行这两个步骤,所以解释型语言的执行速度不快,并且每次执行都离不开语言环境。

编译型语言在程序设计完成之后,使用语言本身提供的编译(Compile)程序与连接(Link)程序把源程序编译连接成为可执行文件(扩展名一般为“.exe”)。可执行文件就能脱离语言设计环境独立运行了。当前比较流行的程序设计语言多数是编译型的。也有些语言既可以解释型地运行程序,也可以对程序进行编译连接。

解释型运行往往用在程序的调试过程中,而设计完成之后就可以把它编译成为独立的可执行文件。

计算机只能识别0、1,并不能能识别其他的语言。程序员在开发的时候,可以使用很多种语言,如c语言,java,python。使用不同的语言开发出来的程序,如果想要执行,那么最终必须要变成机器语言才能执行。那怎么样变成机器语言,我们大家可以找一个翻译。这个翻译就专门负责把编写的代码翻译成机器能够识别的机器语言,叫做编译器,不同的编译器,就负责把不同的语言翻译成计算机能够识别的机器语言来,这个就是编译器的作用。

根据编译器对源代码翻译的方式不同,编译器分成两种类型,一种类型叫编译器。而另一种类型叫做解释器。

使用编译器编译的语言,通常称为编译性语言,而使用解释器解释的语言叫做解释性语什么又是编译性语言,什么又是解释性语言?

这两种语言到底是怎么工作的?最典型的代表就是C语言、C 这种语言都叫做编译性语言。编译性语言是怎么工作的,人们来看c语言或者C 的程序在自己的开发环境内来编写代码。那当程序开发完成之后,成员就把开发完成的源代码统一交给编译器。编译器对所有源代码进行翻译。翻译成机器语言,并且最终保存成一个可执行的文件,当我们需要执行这个文件的时候,在windows下最常见的操作就是双击一下可执行文件的图标,就可以把这个文件交给CPU去执行。编译性语言的特点,程序员在自己的开发环境内开发程序开发完成之后,统一交给编译器。编译器统一进行翻译,并且最终生成一个独立的可执行文件。用户在需要的时候,就可以执行可执行文件看到最终的效果。

解释性语言的特点,python语言就是一个解释性语言,那解释性语言在开发的时候,跟编译性语言并没有太大的区别,成员仍然是在自己的开发环境内来编写代码。假设现在写了三行代码,那这三行代码怎么运行啊?要想运行解释性语言,我们就把这个源程序丢给解释器。解释器拿到源程序之后,会按照从上向下的方式逐一读取代码中央解释器称一行一行来翻译的。首先读出第一行代码,就立刻翻译成机器码。翻译完成之后,就丢给CPU去执行CPU在执行的过程中,解释器在读取第二行代码进行翻译。翻译完成之后,再交给CPU去执行,然后依次类推,从上到下一次读取每行代码读取一行。翻译一行执行一行。

编译性语言是统一编译一次性执行。

解释性语言是一行一行代码进行翻译,翻译一行执行一行,编译性语言最终产生的文件执行速度快,解释性语言执行速度慢。因为最终生成的可执行文件中不需要任何的介入。

解释性语言不同。解释语言在执行的时候,必须是翻译一行执行一行。解释性语言的执行速度就相对慢一些,需要考虑的因素就所谓跨平台,就是我们开发完成的程序,既可以在windows上运行,也可以在linux上运行,还可以在MAC上运行一次编写在任何一个平台上都能运行,这种方式就叫做跨平台。

如果我们使用的编译器是在windows平台上编译的程序,那么最终生成的可执行文件只能在windows平台上运行,它并不能够在linux上运行,并不能也不能在MAC上运行,这个是编译性语言的特点。如果使用某一个操作系统的编译器,那么,这个编译器最终生成的可执行文件就只能在这个操作系统上运行,而不能在其他操作系统上运行。

解释性语言相对来说就简单了,程序员仍按照习惯的方式来编写代码,程序编写完成之后,如果想要执行,如果是windows,就在windows上安装一套windows的解释器,如果想在linux上执行呢,就在linux上安装一套linux的解释器,就是在不同的操作系统上安装不同的解释器。既然在每个操作系统上都已经安装了解释器,那源代码就不需要任何的修改。这个就是解释性语言在跨平台上的优势。至于程序的执行是解释器的工作,只需要在不同操作系统中安装不同的解释器同一份代码就可以在不同操作系统中执行了。

开发完成的源程序要想执行,就必须找一个翻译性语言要找的翻译叫做编译器,解释性语言要找的翻译叫做解释器,而从执行效率上讲,编译性语言执行效率要比解释性语言执行效率高,但是从跨平台来讲解释性语言跨平台能力要比边形语言跨平台能力要强好。

4.面向对象的程序设计语言

随着像Windows这样具有图形用户界面的操作系统的广泛使用,人们又形成了一种面向对象的程序设计思想。这种思想把整个现实世界或是其一部分看做是由不同种类对象(Object)组成的有机整体。同一类型的对象既有共同点,又有各自不同的特性。各种类型的对象之间通过发送消息进行联系,消息能够激发对象做出相应的反应,从而构成了一个运动的整体。采用了面向对象思想的程序设计语言就是面向对象的程序设计语言,当前使用较多的面向对象语言有Visual_Basic、C++、Java等。

面向对象语言:是一类以对象作为基本程序结构单位的程序设计语言,指用于描述的设计是以对象为核心,而对象是程序运行时刻的基本成分。面向对象语言:系统中的基本构件可识认为一组可识别的离散对象,在基本层次关系的不同类中共享数据和操作。

Python是一个完全面向对象的语言,那什么又是面向对象?

面向对象是一种思维方式,同时也是一门程序设计技术。程序员每天的工作是使用自己熟悉的语言来解决一个又一个问题,那在解决问题的时候,有两种方式,第一种方式要解决这个问题,自己一步一步把这个问题解决掉,自己来逐步的解决一个问题。第二种方式就是面向对象的这种解决问题的方法,用面向对象来解决一个问题的时候,通常我们要首先考虑由谁(这里指对象,而其具备解决该问题能力)来做。找一个别人来帮助自己做事情,而我们找到了这个对象,已经具备了解决这个问题的能力。这个对象做完之后,问题也同样得到了解决。这个就是面向对象的解决方法。

第一种方式自己逐步来解决问题的每一个步骤,第二种方式我们来找一个对象替自己做事情,对象又具有做这件事情的能力。

如果开发程序,当然更倾向于第二种方式。找个对象来完成,这个思路就是面向对象的思维方式。在做事情的时候,找一个具有能力的对象,帮我们把问题解决掉就好了。这个就是从思维方式角度所谓面向对象的概念。

python是一个完全面向对象的语言。在python中,无论是函数,模块,数字以及字符串等等等等,全部都是对象。在python中所有的东西都是对象,python这门语言中已经提供有各种各样,具有很强大能力的对象。在工作中遇到不同的问题,就找不同的对象来帮我们解决问题就可以。这个是python面向对象语言的一个特点,同时大家在看第二个特点。Python应用一个强大的标准库,所以强大的标准库在python这门语言中已经内置有非常非常多,是具有强大能力的对象。当在开发时遇到不同的问题,可以在标准库中来找不同的对象,帮我们把问题解决掉就好,在python的标准库中提供有类似于系统管理,网络文本处理等,它的功能还是非常强大的。第三个特点:Python社区提供了大量的第三方模块,什么又是第三方模块?所谓第三方模块就是跟标准库类似的一个库,但是第三方模块并不是由官方来开发的,而是由网络上非常非常多python爱好者来开发的。那这些爱好者为什么要开发第三方模块原因很简单,因为标准估虽然很强大,但是标准库的力量有限,而全世界有非常多的python爱好者以及开发团队或者公司。针对当今市场上最主流的一些应用技术开发有非常多的模块,把自己开发好的这些模块开源出来。这些模块都涉及到哪些领域,分别包括有科学计算,人工智能机器学习,以及web开发大数据等。在python社区中有大量的第三方模块,而这些第三方模块在使用的,基本的方式是跟标准库类似的,python这门语言既有一个能力非常强大的标准库,又有一个非常非常丰富的第三方模块。那么,作为python的成员在开发的时候是不就非常容易了。面向对象的思维方式,就是在做事情之前,先找一个具有能力的对象,帮我们来解决问题。而python的标准库也好。Python第三方模块也好,实际上内置有大量的具有强大能力的对象,我们在使用python进行日常开发时,只需要从标准库中或者第三方模块中找到。能够帮我们解决问题的对象,并且使用对象已经具有的能力,通常就可以快速的把我们日常开发中需要解决的问题搞定了,Python提供有强大的标准库和第三方模块。在开发时,只需要找到相应具有能力的对象,就可以解决日常工作中遇到的问题了。

程序设计语言的支持环境

操作系统是计算机最重要的一类软件,其他程序的运行都要在操作系统支持与控制下进行。设计者编制的源程序并不能直接操作计算机,而要在要具体的程序设计语言的支持下通过操作系统来完成。它们之间如何相互配合,因语言、操作系统、计算机硬件的不同而不同。大多数情况下,编程人员没必要关心程序每一个细节。

‘玖’ 编译程序前三阶段完成工作

编译程序前三阶段完成工作词法分析、语法分析、语义分析和中间代码生成。

编译程序是把用高级程序设计语言或计算机汇编语言书写的源程序,翻译成等价的机器语言格式目标程序的翻译程序,属于采用生成性实现途径实现的翻译程序。编译程序以高级程序设计语言书写的源程序作为输入,而以汇编语言或机器语言表示的目标程序作为输出。

主要功能:

①语法检查:检查源程序是否合乎语法。如果不符合语法,编译程序要指出语法错误的部位、性质和有关信息。编译程序应使用户一次上机,能够尽可能多地查出错误。

②调试措施:检查源程序是否合乎设计者的意图。为此,要求编译程序在编译出的目标程序中安置一些输出指令,以便在目标程序运行时能输出程序动态执行情况的信息,如变量值的更改、程序执行时所经历的线路等。这些信息有助于用户核实和验证源程序是否表达了算法要求。

③修改手段:为用户提供简便的修改源程序的手段。编译程序通常要提供批量修改手段(用于修改数量较大或临时不易修改的错误)和现场修改手段(用于运行时修改数量较少、临时易改的错误)。

‘拾’ c语言中define的用法

C语言是计算机软件领域非常经典的编程语言,unix、linux等众多操作系统均是由C语言编写而成。而在硬件控制、底层驱动等应用领域,C语言更是具有不可替代的作用。下面我就跟你们详细介绍下c语言中define的用法,希望对你们有用。

c语言中define的用法如下:

#define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质,总是在此处产生一些困惑,在编程时误用该命令,使得程序的运行与预期的目的不一致,或者在读别人写的程序时,把运行结果理解错误,这对 C语言的学习很不利。

1. #define命令剖析

1.1 #define的概念

#define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。

该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义。

(1) 简单的宏定义:

#define <宏名><字符串>

例: #define PI 3.1415926

(2) 带参数的宏定义

#define <宏名> (<参数表>) <宏体>

例: #define A(x) x

一个标识符被宏定义后,该标识符便是一个宏名。这时,在程序中出现的是宏名,在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。

1.2 宏替换发生的时机

为了能够真正理解#define的作用,让我们来了解一下对C语言源程序的处理过程。当我们在一个集成的开发环境如Turbo C中将编写好的源程序进行编译时,实际经过了预处理、编译、汇编和连接几个过程,见图1。

源程序预处理器修改后的源程序编译器汇编程序汇编器可重定位的目标程序连接器可执行的目标程序图1C语言的编译过程

其中预处理器产生编译器的输出,它实现以下的功能:

(1) 文件包含

可以把源程序中的#include 扩展为文件正文,即把包含的.h文件找到并展开到#include 所在处。

(2) 条件编译

预处理器根据#if和#ifdef等编译命令及其后的条件,将源程序中的某部分包含进来或排除在外,通常把排除在外的语句转换成空行。

(3) 宏展开

预处理器将源程序文件中出现的对宏的引用展开成相应的宏 定义,即本文所说的#define的功能,由预处理器来完成。

经过预处理器处理的源程序与之前的源程序有所有不同,在这个阶段所进行的工作只是纯粹的替换与展开,没有任何计算功能,所以在学习#define命令时只要能真正理解这一点,这样才不会对此命令引起误解并误用。

2#define使用中的常见问题解析

2.1 简单宏定义使用中出现的问题

在简单宏定义的使用中,当替换文本所表示的字符串为一个表达式时,容易引起误解和误用。如下例:

例1 #define N 2+2

void main()

{

int a=N*N;

printf(“%d”,a);

}

(1) 出现问题:在此程序中存在着宏定义命令,宏N代表的字符串是2+2,在程序中有对宏N的使用,一般同学在读该程序时,容易产生的问题是先求解N为2+2=4,然后在程序中计算a时使用乘法,即N*N=4*4=16,其实该题的结果为8,为什么结果有这么大的偏差?

(2)问题解析:如1节所述,宏展开是在预处理阶段完成的,这个阶段把替换文本只是看作一个字符串,并不会有任何的计算发生,在展开时是在宏N出现的地方 只是简单地使用串2+2来代替N,并不会增添任何的符号,所以对该程序展开后的结果是a=2+2*2+2,计算后=8,这就是宏替换的实质,如何写程序才 能完成结果为16的运算呢?

(3)解决办法:将宏定义写成如下形式

#define N (2+2)

这样就可替换成(2+2)*(2+2)=16

2.2 带参数的宏定义出现的问题

在带参数的宏定义的使用中,极易引起误解。例如我们需要做个宏替换能求任何数的平方,这就需要使用参数,以便在程序中用实际参数来替换宏定义中的参数。一般学生容易写成如下形式:

#define area(x) x*x

这在使用中是很容易出现问题的,看如下的程序

void main()

{

int y=area(2+2);

printf(“%d”,y);

}

按理说给的参数是2+2,所得的结果应该为4*4=16,但是错了,因为该程序的实际结果为8,仍然是没能遵循纯粹的简单替换的规则,又是先计算再替换 了,在这道程序里,2+2即为area宏中的参数,应该由它来替换宏定义中的x,即替换成2+2*2+2=8了。那如果遵循(1)中的解决办法,把2+2 括起来,即把宏体中的x括起来,是否可以呢?#define area(x) (x)*(x),对于area(2+2),替换为(2+2)*(2+2)=16,可以解决,但是对于area(2+2)/area(2+2)又会怎么样 呢,有的学生一看到这道题马上给出结果,因为分子分母一样,又错了,还是忘了遵循先替换再计算的规则了,这道题替换后会变为 (2+2)*(2+2)/(2+2)*(2+2)即4*4/4*4按照乘除运算规则,结果为16/4*4=4*4=16,那应该怎么呢?解决方法是在整个 宏体上再加一个括号,即#define area(x) ((x)*(x)),不要觉得这没必要,没有它,是不行的。

要想能够真正使用好宏定义,那么在读别人的程序时,一定要记住先将程序中对宏的使用全部替换成它所代表的字符串,不要自作主张地添加任何其他符号,完全展 开后再进行相应的计算,就不会写错运行结果。如果是自己编程使用宏替换,则在使用简单宏定义时,当字符串中不只一个符号时,加上括号表现出优先级,如果是 带参数的宏定义,则要给宏体中的每个参数加上括号,并在整个宏体上再加一个括号。看到这里,不禁要问,用宏定义这么麻烦,这么容易出错,可不可以摒弃它, 那让我们来看一下在C语言中用宏定义的好处吧。

3 宏定义的优点

(1) 方便程序的修改

使用简单宏定义可用宏代替一个在程序中经常使用的常量,这样在将该常量改变时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时, 我们可以用较短的有意义的标识符来写程序,这样更方便一些。我们所说的常量改变不是在程序运行期间改变,而是在编程期间的修改,举一个大家比较熟悉的例 子,圆周率π是在数学上常用的一个值,有时我们会用3.14来表示,有时也会用3.1415926等,这要看计算所需要的精度,如果我们编制的一个程序中 要多次使用它,那么需要确定一个数值,在本次运行中不改变,但也许后来发现程序所表现的精度有变化,需要改变它的值, 这就需要修改程序中所有的相关数值,这会给我们带来一定的不便,但如果使用宏定义,使用一个标识符来代替,则在修改时只修改宏定义即可,还可以减少输入 3.1415926这样长的数值多次的情况,我们可以如此定义 #define pi 3.1415926,既减少了输入又便于修改,何乐而不为呢?

(2) 提高程序的运行效率

使用带参数的宏定义可完成函数调用的功能,又能减少系统开 销,提高运行效率。正如C语言中所讲,函数的使用可以使程序更加模块化,便于组织,而且可重复利用,但在发生函数调用时,需要保留调用函数的现场,以便子 函数执行结束后能返回继续执行,同样在子函数执行完后要恢复调用函数的现场,这都需要一定的时间,如果子函数执行的操作比较多,这种转换时间开销可以忽 略,但如果子函数完成的功能比较少,甚至于只完成一点操作,如一个乘法语句的操作,则这部分转换开销就相对较大了,但使用带参数的宏定义就不会出现这个问 题,因为它是在预处理阶段即进行了宏展开,在执行时不需要转换,即在当地执行。宏定义可完成简单的操作,但复杂的操作还是要由函数调用来完成,而且宏定义 所占用的目标代码空间相对较大。所以在使用时要依据具体情况来决定是否使用宏定义。

形式参数不能用带引号的字符串替换。

但是,如果在替换文本中,参数名以#作为前缀则结果将被扩展为 由 实际参数 替换 此实际参数的带引号的字符串。

例如,可以将它与字符串连接运算结合起来编写一个调试打印宏:

#define dprint(expr) printf(#expr “ = %\n”,expr)

使用语句 dprint(x/y);

调用宏时,该宏将被扩展为:printf(“x/y”“ = %\n”,x/y);

其中的字符串被连接起来了,这样便等价于printf(“x/y = %\n”,x/y);

在实际参数中,每个双引号 “ 将被替换为 \” ;反斜杠\将被替换为\\,因此替换后的字符串是合法的字符串常量。

预处理运算符 ## 为宏扩展提供了一种连接实际参数的手段。如果替换文本中的参数与 ## 相邻,则该参数将被实际参数替换,##与前后的空白符将被删除,并对替换后的结果重新扫描。

例如,下面定义的宏paste用于连接两个参数

#define paste(front, back) front ## back

因此,宏调用past(name,1)的结果将建立记号name1.

c语言中没有swap这个函数,C语言不支持重载,也没有模版的概念,所以对于每一种类型,都要写出相应的swap,如

intSwap (int *, int *);

longSwap (long *, long *);

stringSwap (char *, char *);

宏定义swap(t,x,y)以交换t类型的两个参数(要使用程序块结构)。

程序如下:

#include <iostream.h>

#define SWAP(t,x,y) \

{\

t temp = *y;\

*y = *x;\

*x = temp;\

}

main()

{

int a = 10, b = 5;

SWAP(int,&a,&b)

cout << a << endl << b<<endl;

}

用\换行,\的意思是说把下一行看作和这行是同一行.换行必须要反斜杠,而且\后面直接回车,不能有空格。

阅读全文

与在源程序被编译器处理之前相关的资料

热点内容
怎么查移动定向流量使用的app 浏览:80
进入组策略的命令 浏览:137
python数据结构和内存 浏览:25
python软件功能简介 浏览:784
外国程序员一般多少岁退休 浏览:917
怎么看linux和时间服务器 浏览:680
程序员搞笑花名 浏览:501
dota2怎么设置国服服务器地址 浏览:212
单片机高电平驱动 浏览:115
ios多选文件夹 浏览:909
加强行车调度命令管理 浏览:243
服务器已禁用什么意思 浏览:150
部队命令回复 浏览:755
神奇宝贝服务器地图怎么设置 浏览:382
加密算法输出固定长度 浏览:862
程序员去重庆还是武汉 浏览:121
服务器如何撤销网页登录限制 浏览:980
微信公众平台php开发视频教程 浏览:628
怎么看苹果授权绑定的app 浏览:255
压缩机单级压缩比 浏览:380