标签:info 面向对象 模型 表示 c++ 词法分析 head 一点 任务
预处理也称为预编译,它为编译做准备工作,主要进行代码文本的替换工作,用于处理#开头的指令,其中预处理产生编译器的输出。下表是一些常见的预处理指令及其功能。
经过预处理器处理的源程序与之前的源程序会有所不同,在预处理阶段所进行的工作只是纯粹的替换与展开,没有任何功能,所以在学习#define命令时只有阵阵理解这一点,才不会对此命令引起误解并误用。
对于#include<filename.h>,编译器先从标准库路径开始搜索filename.h。
而对于#include"filename.h",编译器先从用户的工作路径开始搜索filename,h,然后取寻找系统路径,使得自定义文件较快。
由于宏定义在预处理阶段进行,主要做的是字符替换工作,所以它存在一些固有的缺陷:
含参数的宏有时完成的是函数实现的功能,但是并非所有的函数都可以被含参数的宏所替代。具体而言,含参数的宏与函数的特点如下:
typedef与define都是替一个对象取一个别名,一次来增强程序的可读性,但是它们在使用和作用上也存在者以下几个方面的不同:
typedef int (*PF)(const char*,const char *);
定义一个指向函数的指针的数据类型PF,其中函数返回值为int,参数为const char *。typedef还由另外一个重要的用途,那就是定义机器无关的类型。例如可以定义一个叫做REAL的浮点类型,在目标机器上它可以获得最高的精度:typedef long double REAL,在不支持long double的机器上,该typedef 是这样的typedef double REAL。#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。
void fun() { #define A int } void gun() { //在这里可以定义A,因为宏替换没有作用域, //如果上面用detypedef,这里就不能调用A }
#define INTPTR1 int* typedef int* INTPTR2; INTPTR1 p1,p2; INTPTR2 p3,p4;
这里INTPTR1 p1,p2进行字符串替换后变成int* p1,p2,要表达的意义是声明一个指针变量和要给整型变量p2.而 INTPTR p3,p4由于INTPTR2是具有含义的,告诉我们是一个指向整型数据的指针,那么p3和p4都为指针变量,这句相当于int* p1,*p2.从这里可以看出,进行宏替换是不含任何意义的替换,仅仅为字符串替换;而用typedef为一种数据类型起别名是带有一定含义的。程序示例如下:
#define INTPTR1 int* typedef int* INTPTR2; int a=1; int b=2; int c=3; const INTPTR1 p1=&a;//表示p1是一个常量指针,既不可以通过p1去修改p1指向的内容,但p1可以指向其他内容。 const INTPTR2 p2=&b;//由于INTPTR2表示是一个指针类型,因此用const去限定,表示封锁了这个指针类型,因此p2是一个指针常量,不可使p2再指向其他内容,但可通过p2修改当前指向的内容。 INTPTR2 const p3=&c;//一个指针常量
宏代码本身不是函数,但使用起来却像函数,预处理器用赋值宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的CALL调用、返回参数、执行return等过程,从而提高了速度。内联函数是代码被插入到调用者代码处的函数。对于C++而言,内联函数也不是万能的,它的使用有限制,它只适合函数体内diamagnetic简单的函数使用,不能包含复杂的结构控制语句(如while、switch),并且内联函数本身不能直接调用递归函数。
两者的区别主要表现在以下几个方面:第一,宏定义是在预处理阶段进行代码替换,而内联函数是在编译阶段插入代码;第二,宏定义没有类型检查,而内联函数有类型检查。
#define只是用来进行单纯的文本替换,define变量的声明周期止于编译期,不分配内存空间,它存在于代码段,在实际程序中他只是一个常数,一个命令中的参数并没有实际的存在;而const常量存在于程序的数据段,并在堆栈中分配了空间,const常量在程序中确确实是的存在,并且可以被调用、传递。
const常量有数据类型,而define常量没有数据类型。编译器可以对const常量进行类型安全检查,而define不行。
由于const修饰的变量可以排除程序之间的不安全因素,保护程序中的常量不被修改,而且对数据类型也会进行相应的检查,极大的提高了程序的健壮性,所以一般更倾向于用const来定义常量类型。
编译是程序执行执行过程中的一个重要步骤,了解程序的编译过程以及编译方式都对程序的开发起着至关重要的作用。
在多到程序环境中,要想将用户源代码变成一个可以在内存中执行的程序,通常分为3个步骤:编译、链接、载入。
编译和链接为将用户程序从硬盘上调入内存并将其转换成可执行程序服务的。用编译器时的compile就是在进行编译,link就是链接,运行程序可以看到。
编译型语言:编译是指在应用源程序执行之前,就将程序源代码“翻译”成目标代码(机器语言),因此其目标程序可以脱离其语言环境独立执行,使用方便、效率较高。但应用程序一旦需要修改,必须先修改源代码,在重新编译生成新的目标文件(*.obj)才能执行,只有目标文件而没有源代码,修改很不方便。现在大多数的编译语言都是编译型的。编译程序将源程序翻译成目标程序后保存在另一个文件中,该目标程序可脱离编译程序直接在计算机上多次运行。C\C++\Fortran\Visual Foxpro等都是编译实现的。
解释性语言:解释型语言的实现中,翻译器并不产生机器代码,而是产生易于执行的中间代码。这种中间代码与机器代码不同,中间代码的解释是由软件支持的,不能直接使用硬件,软件解释器通常会导致执行效率较低。用解释性语言编写的程序是由另一个可以理解中间代码的解释程序执行的。与编译程序不同的是,解释程序的任务是逐一将源程序的语句解释成可执行的机器指令,不需要将源程序翻译成目标代码后再执行。解释程序的优点是当语句出现语法错误时,可以立即发现。而程序员在程序开发期间就能进行校正。对于解释型Basic语言,需要一个专门的解释器解释执行Basic程序,每条语句只有在执行时才被翻译。这种解释性语言每执行一次就翻译一次,效率低下。例如:Tcl、Perl、VBScript、JavaScriptdeng .
需要注意的是,Java是一类特殊的编程语言,Java程序也需要编译,但是却没有直接编译为机器语言,而是编译为字节码,然而Java虚拟机上以解释方式执行字节码。
如果编译器在编译cpp问价,那么cplusplus就会被定义,如果是一个C文件在被编译,那么__STDC__就会被定义。__STDC__是预定义宏,当它被定义后,编译器将按照ANSIC标准来编译C语言程序。
所以,可以采用如下程序示例判断。
#include "stdafx.h" #include<stdio.h> #ifdef _cplusplus #define USING_C 0 #else #define USING_C 1 #endif #include <stdio.h> int main() { if (USING_C) printf("C\n"); else printf("C++\n"); return 0; }
在C编译环境下,程序运行结果如下:
编写C与C++兼容的代码所需的宏如下:
#ifdef _cplusplus extern "C" { #endif //具体的代码 #ifdef _cplusplus } #endif
在上例中,_cplusplus是cpp中的自定义宏,当定义了这个宏时,其表示这是一段cpp的代码。也就是说,上面代码的含义为如果这是一段cpp的代码,那么加入extern "C"{和}处理其中的代码。
考虑到C语言没有重载的概念,所以C编译器编译的程序例,所有函数只有函数名对应的入口,而由于C++支持重载,如果只有函数名对应的入口,则会出现混淆,所以C++编译器编译的程序,应该是函数名+参数类型列表对应到入口。
因为main函数是整个函数的入口,所以main是不能有重载的。如果一个程序只有main()函数,是无法确认是C还是C++编译器编译的,此时可以通过nm来查看函数名入口。例如,函数inf foo(int i,float j),C编译的程序通过nm查看如下:foo 0x567xxxxxx(地址).C++编译程序,通过nm查看为foo(int ,float) 0x567xxxxxx.
C++是一种面向对象编程的语言,支持函数重载,而C语言是面向过程的编程语言,不支持函数重载,所以函数被C++编译后在库中的名字于C语言的不同。如果声明一个C语言函数float f(int a,char b),C++的编译器就会将这个名字变成像_f_int_char之类的东西以支持函数重载。然而C语言编辑器的库一般不执行改转换,所以它的内部名为_f,这样链接器将无法解释C++对函数f()的调用。
C++提供了C语言替代连接说明符号extern "C"来解决名字匹配问题,extern是C/C++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器,其声明的函数和变量可以在模块或其他模块中使用。extern后跟一个字符串来指定想声明的函数的连接类型,后面是函数声明。
extern "C" float f(int a,char b);
该句目的是告诉编译器f()是C连接的,这样C++就不会转换函数名。标准的连接类型指定符有"C""C++"两种,但可以选择同样的方法支持其他语言。如果有一组替代连接的声明,可以把它们放在花括号里:
extern "C" { float f(int a,char b); ..//其他函数 } 或者写成 extern "C" { #include "Myheader.h" ..//其他C头文件 }
这就告诉C++编译器,函数f是采用C语言方式链接的,应用到库中找名字_f而不是找_f_int_char。C++编译器已经对C标准库的头文件做了extern "C"处理,所以可以用#include直接引用这些头文件。
标签:info 面向对象 模型 表示 c++ 词法分析 head 一点 任务
原文地址:https://www.cnblogs.com/noticeable/p/9310798.html