当前位置:文档之家› 预处指令 #if #esle #endif #ifdef(详细)

预处指令 #if #esle #endif #ifdef(详细)

预处指令 #if #esle #endif #ifdef(详细)
预处指令 #if #esle #endif #ifdef(详细)

转换单元可采用下列形式通信:

* 调用具有外部连接的函数。

* 调用具有外部连接的类成员函数。

* 直接更改具有外部连接的对象。

* 文件的直接更改。

* 内部外理通信(仅限于基于Microsoft Windows的应用程序)。以下是编译器翻译文件的各个阶段:

字符映射

源文件中的字符被映射为内部源代码的形式。?此阶段三字母序列被转换为单字符的内部表现形式。

行拼接

在此阶段,源文件中所有以反斜杠(/)结尾且其后紧跟一换行符的行,?将与下一行连接,从而由物理行生成逻辑行。所有非空源文件结束于一个前面没有反斜杠的换行符。

语言符号化

此阶段源文件被分为预处理语言符号和空白字符。源文件中每个注释被用一个空白字符代替。换行符被保留。

预处理

此阶段执行预处理指令并将宏扩展至源文件,#i nclude语句调用对所有包括文本启动前面三个翻译步骤开头的翻译过程。

字符集映射

所有的源字符集成员和转义序列将转换为执行字符集中的等价形式,对于Microsoft C和

C++来说,源字符集和执行字符集都是ASCII码。

字符串合并

所有相邻的字符串和宽字符文字都将被合并。例如:“String”“concatenation”合并为“Stringconcatenation”。

翻译

所有的语言符号将按语法和语义规则进行分析;这些语言符号被转换为目标代码。

链接

此阶段所有的外部引用被分解以生成一个可执行程序或一个动态链接库。

编译器在翻译过程中遇到语法错误时,将发出一个警告或错误信息。

链接器分解所有的外部引用,并把一个或多个分开处理的转换单元和标准库联接起来,以生

成一个可执行程序或动态链接库(DLL)。

预处理器指令

预处理器指令如#define和#ifdef,?一般被用在不同的运行环境下,使源程序易于更改和编译。源文件中的指令指示预处理器执行特有的行为。例如,预处理器可替换文本中的语言符号,将其它的文件内容插入源文件中,或移走文本的一部分以抑制文件中某部分的编译。预处理器行在宏扩展之前被识别且执行。不过,如果宏扩展看起来象一个预处理器指令,该命令将不能被预处理器识别。

除转义序列之外,预处理器语句采用与源文件语句相同的字符集。在预处理器语句中的字符集和可执行程序的字符集是一样的。预处理器也可识别负字符值。预处理器可识别如下指令:

#define #error #import #undef

#elif #if #i nclude

#else #ifdef #line

#endif #ifndef #pragma

数字符号(#)是包含预处理器指令的行中的第一个非空白字符。?空白字符可出现在数字符号和指令的第一个字母之间。某些指令包含参量和值。指令之后的任何文本(除作为指令一部分的参量或值之外)必须放在单行注释分界符(//)之后或注释分界符(/* */)之间。

预处理器指令可出现在源文件的任何地方,但它们仅用于源文件的剩余部分。

#define指令

可以用#define指令给程序中的常量取一个有意义的名称,其语法的两种形式如下:

语法

#define 标识符语言符号字符串

opt#define 标识符[(标识符opt,...,标识符opt)]?语言符号字符串opt

#define指令用语言符号字符串替换源文件中一个标识符的所有出现,标识符仅在它形成一

个语言符号时被替换(参见“Microsoft Visual C++ 6.0?参考库”的“Microsoft Visual C++ 6.0?语言参考手册”卷的第1章“词法规定”中的“语言符号”)。例如,?若标识符出现在一个注释、一个字符串或作为一个长标识符的一部分之中,它就不被替换。

一个不带语言符号字符串的#define指令将移走源文件中每次标识符的出现。标识符保留其定义且能用#defined和#ifdef测试。

语言符号字符串参量由一系列语言符号组成,如关键字、常量或完整的语句。一个或多个空白字符可将语言符号字符串和标识符分开。空白字符不会被认为是被替换文本的一部分,文本最后语言符号之后的空白也不会认为是替换文本的一部分。

形式参数名称出现在语言符号字符串中以标志出实际值被替换的位置,?每个参数名称可在

语言符号字符串中出现多次,且可以以任何次序出现。调用时参量的数目必须与宏定义的参数数目相匹配。圆括号的自由运用可确保正确地说明复杂的实际参量。

用第二种语法形式可创建类似函数的宏。这种形式接受一个用圆括号括起的可选参数表。在最初定义之后引用该标识符,可以使用实际参量替代形式参数的语言符号字符串参量的形式,来替换标识符(标识符opt,...,标识符opt)的每次出现。

表中的形式参数必须用逗号隔开。该表中的每个名称都必须是唯一的,且此参量表必须包括在圆括号中,标识符和左边的圆括号之间不能有空格。对于占用多行的长指令可使用行连接,把反斜杠(/)放在换行符前。?形式参数名称的范围延伸到结束语言符号字符串的换行符。

当一个宏以第二种语法形式定义时,参量表后的文本实例就构成一个宏调用。在源文件中,一个标识符实例后的实际参量必须与宏定义的相应形式参数匹配。每个语言符号字符串之前无字符串化(#)、字符化(#@)或语言符号粘贴(##)操作符,或其后无##操作符的形式参量,都被

相应的实际参量所替换。在指令替换形式参数之前,实际参量中的任何宏都将被扩展(本章之后的“预处理器操作符”中将介绍这些操作符)。

以下带参量宏的例子说明了#define语法的第二种形式:

//定义光标行的宏

#define CURSOR(top,bottom) ((top) << 8) | bottom))

//获取指定范围中的一个随机整数的宏

#define getrandom(min,max) /

((rand()%(int)(((max)+1)-(min)))+(min))

有副作用的参量有时会导致宏产生不希望的结果。一个给定的形式参量在语言符号字符串中可能出现多次。如果该形式参数被一个有副作用的表达式所替换,则该表达式及其副作用,可能被求值多次(参见本章后面“语言符号粘贴操作符##”中的例子)。

#undef指令可使一个标识符的预处理器定义失效。有关的更多信息参见#undef指令。若一个被定义的宏名称出现在语言符号字符串中(即使是另一个宏扩展的结果),它将不被扩展。

除非第二次定义(#define)宏与原定义完全相同,否则重定义一个已定义过的宏将产生一个错误信息。

Microsoft特殊处

Microsoft C/C++允许一个宏的重定义,但会产生一个警告信息,说明新的定义与原定义相同。ANSI C认为宏的重定义是错误的。例如,下面的宏对C/C++是相同的,但会产生一个警告信息:

#define test(f1,f2) (f1*f2)

#define test(a1,a2) (a1*a2

)Microsoft特殊处结束

这个例子用于说明#define指令:

#define WIDTH 80

#define LENGTH (WIDTH+10)

第一个说明定义标识符WIDTH为整形常量80,且用WIDTH和整形常量10定义LENGTH。LENGTH的每次出现都用(WIDTH+10)所替换,接着,WIDTH+10的每次出现都用表达式(80+10)替换。WIDTH+10的圆括号非常重要,因为它们决定着如下语句的解释。

var=LENGTH*20;

经预处理后该语句变为:

var=(80+10)*20;

求得值为1800,若无括号,结果为:

var=80+10*20其值为280。

Microsoft特殊处

在文件开头用/D编译器选项定义宏和常量,和用一个#define预处理指令效果是一样的。能用/D选项定义的宏可达30个。

Microsoft特殊处结束

#error指令

采用error指令可产生编译错误信息。

语法

#error 语言符号字符串

错误消息包括语言符号字符串参量,且从属于宏扩展。这些指令对于检测程序的前后矛盾和预处理时的违犯约束是非常有用的,以下例子说明了预处理时的出错处理:

#if !defined(__cplusplus)

#error C++ complier required.

#endif

当遇到#error指令时,编译终止。

#if,#elif,#else和#endif指令

#if、#elif、#else和#endif指令控制源文件中某部分的编译。如果表达式(#if之后)有一个非0值,则紧跟在#if指令之后的行组将保留在转换单元中。

语法

条件的:

if部分elif部分opt else部分opt endif行

if部分:

if行文

本if行:

#if 常量表达式

#ifdef 标识符

#ifndef 标识符

elif部分:

elif行文本

elif部分elif行文本

elif行:

#elif 常量表达式

else部分:

else行文本

else行:

#else

endif行:

#endif

源文件中每个#if指令都必须与最近的一个#endif相匹配。在#if和#endif指令之前的#elif指令的数目是不限的,但最多只能有一个#else指令。

#else必须是#endif之前的最后一个指令。#if、#elif、#else和#endif指令可嵌套在其它#if 指令的文本部分。每个嵌套的#else、#elif或#endif指令应属于前面最近的一个#if指令。

所有的条件编译指令,如#if和#ifdef,必须与文件结束前最近的#endif指令匹配;否则,将产生

一个错误消息。当条件编译指令包括在包含文件中时,他们必须满足相同的条件:在包含文件结尾没有不匹配的条件编译指令。

宏替换应在#elif命令后的命令行部分内进行,因此一个宏调用可用在常量表达式中。

预处理器选择一个给定文本的出现之一作进一步的处理。文本中指定的一个块可以是文本的任何序列。它可能占用一行以上。通常该文本是对于编译和预处理器有意义的程序文本。

预处理器处理选择文本并将其传送给编译器。若该文本包含预处理器指令,预处理器将执行这些指令。编译器只编译预处理器选定的文本块。

预处理器通过求值每个#if或#elif指令之后的常量表达式直到找到一个为真(非0)的常量表达式来选择单个文本项。预处理器选择所有的文本(包括以#开头的其它预处理器指令)直到它关联的#elif、#else或#endif。

如果常量表达式的所有出现都为假,或者如果没有#elif指令,预处理器将选择#else后的文本块。如果#else被忽略,且所有#if块中的常量表达式都为假,则不选择任何文本块。

常量表达式是一个有以下额外限制的整型常量表达式:

* 表达式必须是整型且可以包括整型常量,字符常量和defined操作符。

* 表达式不能使用sizeof或一个类型造型操作符。

* 目标环境不能表示整数的所有范围。

* 在翻译表示中,int类型和long类型以及unsigned int类型和unsigned long类型是相同的。

* 翻译器可将字符常量翻译成一组不同于目标环境的代码值。为了确定目标环境的属性, 应在为目标环境建立的应用程序中检测LIMITS.H的宏值。

* 表达式不需执行所有的环境查询,但需与目标计算机的执行过程细节隔离开。

预处理器操作符defined可用于特殊的常量表达式,语法如下:

语法

defined(标识符)

defined 标识符

若此标识符当前已定义,则该常量表达式被认为是真(非0);否则,条件为假(0)。一个定义为空文本的标识符可认为已定义。defined指令只能用于#if和#endif指令。

在如下例子中,#if和#endif指令控制着三个函数调用中某一个的编译:

#if defined (CREDIT)

credit();

#elif defined(DEBIT)

debit();

#else

printerror();

#endif

若标识符CREDIT已定义,则对于credit的函数调用被编译。若标识符DEBIT被定义,则对于debit的函数调用被编译。若未定义任何标识符,?将编译对于printerror的函数调用。

注意,在C和C++中,CREDIT和credit是不同的标识符,因为它们的大小写不一样。

如下例子中的条件编译语句给出了一个名称为DLEVEL的已定义的符号常量:

#if DLEVEL > 5

#define SIGNAL 1 #if STACKUSE == 1 #define STACK 200 #else

#define STACK 100 #endif#else

#define SIGNAL 0 #if STACKUSE==1 #define STACK 100 #else

#define STACK 50 #endif

#endif

#if DLEVEL==0

#define STACK 0

#elif DLEVEL==1

#define STACK 100 #elif DLEVEL > 5 display(debugptr)

;#else

#define STACK 200

#endif

第一个#if块中有两组嵌套的#if、#else和#endif指令。第一组指令仅当DLEVELl>5为真时执行;否则,执行#else之后的语句。

第二组中的#elif和#else指令选择基于DLEVEL值的四个选项之一。常量STACK依据DLEVEL定义为0,100或200。若DLEVEL大于5,则编译语句:

#elif DLEVEL > 5

display(debugptr);

且此时不定义STACK。

条件编译一般用于防止同一头文件的多重包含。C++?中在头文件内经常定义类的位置,可使用如下结构来防止多次定义。

//EXAMPLE.H例子头文件

#if !defined(EXAMPLE_H)

#define ExampleE_H

class Example

{

...

};

#endif //!defined(EXAMPLE_H)

上面的代码用于检查符号常量EXAMPLE_H是否已定义。若已定义,该文件就已被包括且不需再处理;如果未定义,常量EXAMPLE_H将被定义,以标记EXAMPLE.H为已经处理。

Microsoft特殊处

条件编译表达式被看作为signed long值,且这些表达式与C++中的表达式采用相同的规则求值。例如,表达式:

#if 0xFFFFFFFFL > 1UL

为真。

Microsoft特殊处结束

#ifdef和ifndef指令

#ifdef和#ifndef指令与使用defined(标识符)操作符的作用是一样的。

语法

#ifdef 标识符

#ifndef 标识符

等同于

#if defined 标识符

#if !defined 标识符

#if指令能用的任何地方都可以用#ifdef和#ifndef指令。当标识符已被定义时,#ifdef标识符语句等同于#if 1;而当标识符未定义或用#undef指令对其反定义时,该语句等同于#if 0。这些指令仅用于检查C或C++源代码中是否出现该标识符,而不是用于检查C或C++源程序中该标识符的说明。

提供这几个指令只为了与该语言的老版本兼容。目前的趋势是偏向于采用defined(标识符)定义常量表达式的#if指令。

#ifndef指令测试与#ifdef相反的条件。若标识符未定义(或已用#undef反定义),其条件为真(非0);反之,条件为假(0)。

Microsoft特殊处

可以使用/D选项从命令行传送标识符,采用/D选项至多可以指定30个宏。检查一个定义是否存在是非常有用的,因为定义可从命令行传送。例如:

//prog.cpp

#ifndef test //这三个语句放在你的代码中

#define final

#endif

CL /Dtest prog.cpp //这是编译的命令

Microsoft特殊处结束

#import 指令

C++特殊处

#import指令用于从一个类型库中结合信息。该类型库的内容被转换为C++类,主要用于描述COM界面。

语法

#import "文件名" [属性]

#import <文件名> [属性]

属性:

属性1,属性2,...

属性1 属性2 ...

文件名是一个包含类型库信息的文件的名称。一个文件可为如下类型之一:

* 一个类型库(.TLB或.ODL)文件。

* 一个可执行(.EXE)文件。

* 一个包含类型库资源(如.OCX)的库文件(.DLL)。

* 一个包含类型库的复合文档。

* 其它可被LoadTypeLib API支持的文件格式。

文件名之前可以有一个目录规格。文件名必须是一个已存在文件的名称。?两种格式的区别是当路径未完全说明时,预处理器检索类型库文件的顺序不同。

动作

语法格式

引号格式这种格式让预处理器首先搜索与包含#import语句的文件同一目录的类型库文件,然后在所有包括(#i nclude)该文件的目录中搜索,最后在如下路径中搜索

尖括号格式这种格式指示预处理器沿以下路径搜索类型库文件

编译器在以下目录中搜索已命名的文件:

1. PATH环境变量路径表。

2. LIB环境变量路径表。

3. 用/I(额外的包括目录)编译器选项指定的路径。#import可以任选地包含一个或多个属性。这些属性使编译器改变类型库头文件的内容。一个反斜杠(/)符可用在一个单一的#import语句中包含额外的行,例如:

#import "test.lib" no_namespace /

rename("OldName","NewName")

#import属性列出如下:

#import指令可创建两个在C++源代码中重构类型库内容的头文件,第一个头文件和用Microsoft接口定义语言(MIDL)编译器生成的头文件类似,但有额外的编译器生成代码和数据。第一个头文件与类型库有相同的基本名,其扩展名为.TLH。第二个头文件也有与类型库相同的基本名,其扩展名为.TLI。它包括了编译器生成成员函数的实现,且被包含在(#i nclude)的第一个头文件内。

两个头文件都在用/Fo(命名对象文件)选项指定的输出目录中。随后它们被读出和编译,就像第一个头文件被#i nclude指令命名一样。

以下是伴随#import指令的编译器优化:

* 头文件被创建时,将被分配与类库相同的时间标志。

* 处理#import时,编译器首先测试头文件是否存在,是否过期。若条件为真,就不需重新创建。

* 编译器延迟对于OLE子系统的初始化,直到碰到第一个#import命令。

#import指令也可参与最小重建且可被置于一个预编译头文件中。

基本类型库头文件

基本类型库头文件由七个部分组成:

1. 头部固定正文:由注释、COMDEF.H(定义用在头部的一些标准宏)的#i nclude语句和其它繁杂的安装信息组成。

2.前向引用和类型定义:由象struct IMyinterface之类的结构说明和用于一些TKIND_ALIAS 项的类型定义组成。

3.灵敏指针说明:模块类_com_ptr_t是一个封装接口指针和消除调用AddRef、Release 和QueryInterface函数需求的灵敏指针实现。此外,它隐藏了创建一个新COM对象中的CoCreateInstance调用。此部分采用宏语句_COM_SMARTPTR_TYPEDEF将COM接口的类型定义创建为_com_ptr_t模板类的模板特例化。例如,对于界面IFoo,.TLH文件包含有:

_COM_SMARTPTR_TYPEDEF(IFoo,_ _uuidof(IFoo));

编译器将其扩展为:type def _com_ptr_t<_com_IIID> IFooPtr;

类型IFooPtr可以用在原始的界面指针IFoo*的地方。结果,就不需调用各种IUnknown成员函数。

4. 类型信息(typeinfo)说明:主要由类定义和其它项组成,这些项说明由ITyptLib:GetTypeInfo 返回的单个的信息类型项目。在这部分,每个来自于类型库的信息类型都以一种依赖于TYPEKIND信息的格式反映在该头部。

5. 任选旧式GUID定义:包含命名的GUID常量的初始化过程,这些定义是格式

CLSID_CoClass和IID_Interface的名称,与那些由MIDL编译器产生的类似。

6. 用于第二个类型库头部的#i nclude语句。

7. 结尾固定正文:目前包括#pragma pack(pop)。

以上这些部分除头部固定正文和结尾固定正文部分之外,都被包括在原来的IDL文件中以library语句指定其名称的名称空间中。你可以通过用名称空间显式限定或包括如下语句从类型库头部使用该名称。

using namespace MyLib

在源代码的#import语句之后立即

名称空间可用#import指令的no_namespace属性来阻止。但阻止的名称空间可能导致名称冲突。名称空间也可用rename_namespace属性重新换名。

编译器提供完全路径给需要依赖当前正在处理的类型库的任何类型库。路径以注释格式写入到由编译器为每个处理的类型库生成的类型库头部(.TLH)。

如果一个类型库包含了对其它类型库定义的类型引用,.TLH文件将包括以下注释:

//

//Cross-referenced type libraries:

//

//#import "c:/path/typelib0.tlb"

//

在#import注释中的实际文件名是存储在寄存器中交叉引用的类型库全路径。如果你遇到由于省略类型定义的错误时,检查.TLH头部的注释,看哪一种依赖类型库需要先输入。在编译该.TLI文件时可能的错误有语法错误(例如C2143,C2146,C2321)、C2501(缺少说明指示符)或C2433(在数据说明中禁止′inline′)。

你必须确定哪些依赖注释是不被系统头部给出的,而是在依赖类型库的#import指令前的某处给出一个#import指令以消除这些错误。

exclude属性exclude(“称1”[,“名称2”,...])

名称1

被排斥的第一个项

名称2

被排斥的第二个项(如有必要)

类型库可能包含在系统头部或其它类型库内定义的项的定义。该属性可用于从生成的类型库头文件中排斥这些项。这个属性可带任意数目的参量,每个参量是一个被排斥的高级类型库项目:

high_method_prefix属性

high_method_prefix("Prefix")

Prefix

被使用的前缀

在缺省的情况下,高级错误处理属性和方法用一个无前缀命名的成员函数来展示。这个名称来自于类型库。high_method_prefix属性说明一个前缀以用于命名这些高级属性和方法。

high_property_prefixes属性

high_property_prefixes("GetPrefix,""PutPrefix,""PutRefPrefix")

GetPrefix

用于propget方法的前缀

用于propput方法的前缀

PutRefPrefix

用于propputref方法的前缀

在缺省情况下,高级错误处理方法,如propget、propput和propputref,分别采用以前缀Get、Put和PutRef命名的成员函数来说明。high_property_prefixes属性用于分别说明这三种属性方法的前缀。

implementation_only属性

implementation_only属性禁止.TLH头文件(基本头文件)的生成。?这个文件包括了所有用于展示类型库内容的说明。该.TLI头文件和wrapper成员函数的实现,将被生成且包含在编译过程中。

当指定该属性时,该.TLI头部的内容将和用于存放普通.TLH头部的内容放在相同的名称空间。此外,该成员函数不会作为联编说明。implementation_only属性一般希望与

no_implementation属性配对使用,以跟踪预编译头文件(PCH)之外的实现。一个有

no_implementation属性的#import语句被置于用来创建pch的源区域中,结果PCH将被一些源文件所用。一个带implementation_only属性的#import语句随后被用在PCH区域之外。在一个源文件里只需用一次这种语句。这将生成不需对每个源文件进行额外重编译的所有必要的wrapper成员函数。

注意:一个#import语句中的implementation_?only属性必须和相同类型库中

no_implementation属性的另一个#import语句配套使用。否则,将产生编译错误。这是因为带no_implementation属性的#import语句生成的wrapper类定义需要编译implementation_only属性生成的语句实现。

include(...)属性

Include(名称1[,名称2,...])

名称1

第一个被强制包含的项

第二个被强制包含的项(如果必要)

类型库可能包含在系统头部或其它类型库中定义的项的定义。#import指令试图用自动排斥这些项来避免多重定义错误。若这些项已经被排斥,象警告C4192所指出的那样,且它们不应该被排斥,则这个属性可用于禁止自动排斥。该属性可带任意数目的参量,每个参量应是被包括的类型库项的名称。

inject_statement属性

inject_statement("source_text")

source_text

被插入到类型库头文件的源文本。

inject_statement属性将其参量作为源文本插入类型库头部。此文本被置于包括头文件中类型库内容的名称空间说明的起始处。

named_guids属性

named_guids属性让编译器定义和初始化模板LIBID_MyLib、CLSID_MyCoClass、

IID_MyInterface和DIID_MyDispInterface的旧式格式的GUID变量。

no_implementation属性

该属性阻止.TLI头文件的生成,这个文件包含wrapper成员函数的实现。如果指定这个属性,则展示类型库项说明的.TLH头将生成没有一个#i nclude语句包括该.TLI头文件。

该属性与implementation_only属性配套使用。

no_auto_exclude属性

类型库可能包括在系统头部或其它类型库中定义的项的定义。#import试图通过自动排斥这些项来避免多重定义错误。当这样做时,每个被排斥的项都将生成一个C4192警告信息。你可禁止这个属性使用自动排斥。

no_namespace属性

#import头文件中的类型库内容一般定义在一个名称空间里。名称空间的名称在原来IDL文件的library语句中指定。如果指定no_namespace属性,编译器就不会生成这个名称空间。

如果你想使用一个不同的名称空间,应代替使用rename_namespace属性。

raw_dispinterfaces属性

raw_dispinterfaces属性让编译器生成一个低级wrapper函数。该函数用于调用IDispatch::Invoke和返回HRESULT错误代码的dispinterface方法和属性。如果未指定此属性,则只生成高级wrapper,它在失败时丢弃该C++异常。

raw_interfaces_only属性

raw_interfaces_only属性禁止生成错误处理wrapper函数以及使用这些wrapper函数的_ _declspec(属性)说明。

raw_interfaces_only属性也导致删除在命名non__property函数中的缺省前缀。通常该前缀是raw_。若指定此属性,函数名称将直接从类型库中生成。该属性只允许展示类型库的低级内容。

raw_method_prefix属性

raw_method_prefix("Prefix")

Prefix

被使用的前缀

用raw_作为缺省前缀的成员函数展示低层属性和方法,以避免与高级错误处理成员函数的

名称冲突。raw_method_prefix属性用于指定一个不同的前缀。注意: raw_method_prefix 属性的效果不会因raw_method_prefix属性的存在而改变。在说明一个前缀

时,raw_method_prefix总是优先于raw_interfaces_only。若两种属性用在同一个#import语句中时,则采用raw_method_prefix指定的前缀。

raw_native_types属性

在缺省情况下,高级错误处理方法在BSTR和VARIANT数据类型和原始COM界面指针的地方使用COM支持类_bctr_t和_variant_t。这些类封装了分配和取消分配这些数据类型的

C语言习题集(预处理命令篇)

第六章预处理命令 6.1 选择题 1.下面叙述中正确的是()。 A. 带参数的宏定义中参数是没有类型的 B. 宏展开将占用程序的运行时间 C. 宏定义命令是C语言中的一种特殊语句 D. 使用#include命令包含的头文件必须以“.h”为后缀 2.下面叙述中正确的是()。 A. 宏定义是C语句,所以要在行末加分号 B. 可以使用#undef命令来终止宏定义的作用域 C. 在进行宏定义时,宏定义不能层层嵌套 D. 对程序中用双引号括起来的字符串内的字符,与宏名相同的要进行置换 3.在“文件包含”预处理语句中,当#include后面的文件名用双引号括起时,寻找被包含文件的方式为()。 A. 直接按系统设定的标准方式搜索目录 B. 先在源程序所在目录搜索,若找不到,再按系统设定的标准方式搜索 C. 仅仅搜索源程序所在目录 D. 仅仅搜索当前目录 4.下面叙述中不正确的是()。 A. 函数调用时,先求出实参表达式,然后带入形参。而使用带参的宏只是进行简单的 字符替换 B. 函数调用是在程序运行时处理的,分配临时的内存单元。而宏展开则是在编译时进 行的,在展开时也要分配内存单元,进行值传递 C. 对于函数中的实参和形参都要定义类型,二者的类型要求一致,而宏不存在类型问 题,宏没有类型 D. 调用函数只可得到一个返回值,而用宏可以设法得到几个结果 5.下面叙述中不正确的是()。 A. 使用宏的次数较多时,宏展开后源程序长度增长。而函数调用不会使源程序变长 B. 函数调用是在程序运行时处理的,分配临时的内存单元。而宏展开则是在编译时进 行的,在展开时不分配内存单元,不进行值传递 C. 宏替换占用编译时间 D. 函数调用占用编译时间 6.下面叙述中正确的是( )。 A. 可以把define和if定义为用户标识符 B. 可以把define定义为用户标识符,但不能把if定义为用户标识符 C. 可以把if定义为用户标识符,但不能把define定义为用户标识符 D. define和if都不能定义为用户标识符 7.下面叙述中正确的是()。 A.#define和printf都是C语句 B.#define是C语句,而printf不是 C.printf是C语句,但#define不是 D.#define和printf都不是C语句

c语言中预编译指令的应用

#if #ifdef和#ifndef的用法和区别 #if #ifdef和#ifndef用法 移位运算符的优先级高于条件运算符,重载是不能改变运算符优先级的,这点要注意,所以代码应当像下面这样调整,写宏的时候一定要注意优先级,尽量用括号来屏蔽运算符优先级。#define MAXIMUM(x,y) ((x)>(y)?(x):(y)) #define MINIMUM.... #include #define MAX #define MAXIMUM(x,y) x>y?x:y #define MINIMUM(x,y) x

汇编语言知识大全

第一章基础知识: 一.机器码:1.计算机只认识0,1两种状态。而机器码只能由0,1组成。故机器码相当难认,故产生了汇编语言。 2.其中汇编由三类指令形成:汇编指令(有机器码对应),伪指令,其他符号(编译的时候有用)。 每一总CPU都有自己的指令集;注意学习的侧重点。 二.存储器:1.存储单元中数据和指令没任何差别。 2.存储单元:Eg:128个储存单元(0~127)128byte。 线: 1.地址总线:寻址用,参数(宽度)为N根,则可以寻到2^N个内存单元。 据总线:传送数据用,参数为N根,一次可以传送N/8个存储单元。 3.控制总线:cpu对元器件的控制能力。越多控制力越强。 四.内存地址空间:1.由地址总线决定大小。 2.主板:cpu和核心器件(或接口卡)用地址总线,数据总线,控制总 线连接起来。 3.接口卡:由于cpu不能直接控制外设,需通过接口卡间接控制。

4.各类存储器芯片:RAM,BIOS(主板,各芯片)的ROM,接卡槽的 RAM CPU在操控他们的时候,把他们都当作内存来对待,把他们总的看作一个由 若干个存储单元组成的逻辑存储器,即我们所说的内存地址空间。 自己的一点理解:CPU对内存的操作是一样的,但是在cpu,内存,芯片之间的硬件本身所牵扯的线是不同的。所以一些地址的功能是对应一些芯片的。 第二章寄存器 引入:CPU中含有运算器,寄存器,控制器(由内部总线连接)。而寄存器是可以用来指令读写的部件。8086有14个寄存器(都是16位,2个存储空间)。 一.通用寄存器(ax,bx,cx,dx),16位,可以分为高低位 注意1.范围:16位的2^16-1,8位的2^8-1 2.进行数据传送或运算时要注意位数对应,否则会报错 二.字:1. 1个字==2个字节。 2. 在寄存器中的存储:0x高位字节低位字节;单元认定的是低单元 数制,16进制h,2进制b

编译预处理

第九章编译预处理 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+x main() { 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 #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)7 6 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++);

(完整word版)汇编语言常用指令大全,推荐文档

MOV指令为双操作数指令,两个操作数中必须有一个是寄存器. MOV DST , SRC // Byte / Word 执行操作: dst = src 1.目的数可以是通用寄存器, 存储单元和段寄存器(但不允许用CS段寄存器). 2.立即数不能直接送段寄存器 3.不允许在两个存储单元直接传送数据 4.不允许在两个段寄存器间直接传送信息 PUSH入栈指令及POP出栈指令: 堆栈操作是以“后进先出”的方式进行数据操作. PUSH SRC //Word 入栈的操作数除不允许用立即数外,可以为通用寄存器,段寄存器(全部)和存储器. 入栈时高位字节先入栈,低位字节后入栈. POP DST //Word 出栈操作数除不允许用立即数和CS段寄存器外, 可以为通用寄存器,段寄存器和存储器. 执行POP SS指令后,堆栈区在存储区的位置要改变. 执行POP SP 指令后,栈顶的位置要改变. XCHG(eXCHanG)交换指令: 将两操作数值交换. XCHG OPR1, OPR2 //Byte/Word 执行操作: Tmp=OPR1 OPR1=OPR2 OPR2=Tmp 1.必须有一个操作数是在寄存器中 2.不能与段寄存器交换数据 3.存储器与存储器之间不能交换数据. XLAT(TRANSLATE)换码指令: 把一种代码转换为另一种代码. XLAT (OPR 可选) //Byte 执行操作: AL=(BX+AL) 指令执行时只使用预先已存入BX中的表格首地址,执行后,AL中内容则是所要转换的代码. LEA(Load Effective Address) 有效地址传送寄存器指令 LEA REG , SRC //指令把源操作数SRC的有效地址送到指定的寄存器中. 执行操作: REG = EAsrc 注: SRC只能是各种寻址方式的存储器操作数,REG只能是16位寄存器 MOV BX , OFFSET OPER_ONE 等价于LEA BX , OPER_ONE MOV SP , [BX] //将BX间接寻址的相继的二个存储单元的内容送入SP中 LEA SP , [BX] //将BX的内容作为存储器有效地址送入SP中 LDS(Load DS with pointer)指针送寄存器和DS指令 LDS REG , SRC //常指定SI寄存器。 执行操作: REG=(SRC), DS=(SRC+2) //将SRC指出的前二个存储单元的内容送入指令中指定的寄存器中,后二个存储单元送入DS段寄存器中。

C中的预处理命令

C中的预处理命令是由ANSIC统一规定的,但它不是C语言的本身组成部分,不能直接对它们进行编译,因为编译程序无法识别它们。必须对程序进行通常的编译(包括词法和语法分析,代码生成,优化等)之前,先对程序中这些特殊的命令进行“预处理”,例如:如果程序中用#include命令包含一个文件“stdio.h”,则在预处理时,将stdio.h文件中的实际内容代替该命令。经过预处理后的程序就像没有使用预处理的程序一样干净了,然后再由编译程序对它进行编译处理,得到可供执行的目标代码。现在的编译系统都包括了预处理,编译和连接部分,在进行编译时一气呵成。我们要记住的是预处理命令不是C语言的一部分,它是在程序编译前由预处理程序完成的。 C提供的预处理功能主要有三种:宏定义,文件包含,条件编译。它们的命令都以“#”开头。 一,宏定义:用一个指定的标识符来代表一个字符串,它的一般形式为: #define 标识符字符串 #define PI 3.1415926 我们把标识符称为“宏名”,在预编译时将宏名替换成字符串的过程称为“宏展开”,而#define 是宏定义命令。 几个应该注意的问题: 1,是用宏名代替一个字符串,也就是做简单的置换,不做正确性检查,如把上面例子中的1写为小写字母l,预编译程序是不会报错的,只有在正式编译是才显示出来。 2,宏定义不是C语句,不必在行未加分号,如果加了分号则会连分号一起置换。 3,#define语句出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束,通常#define命令写在文件开头,函数之前,作为文件的一部分,在此文件范围内有效。4,可以用#undef命令终止宏定义的作用域。如: #define PI 3.1415926 main(){ } #undef PI mysub(){ } 则在mysub中PI 不代表3.1415926。 5,在进行宏定义时,可以引用已定义的宏名,可以层层置换。 6,对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行置换。 7,宏定义是专门用于预处理命令的一个专有名词,它与定义变量的含义不同,只做字符替换不做内存分配。 带参数的宏定义,不只进行简单的字符串替换,还进行参数替换。定义的一般形式为:#define 宏名(参数表)字符串 如:#define S(a,b) a*b,具体使用的时候是int area; area=(2,3); 对带参数的宏定义是这样展开置换的:在程序中如果有带参数的宏(如area=(2,3)),则按#define命令行中指定的字符串从左到右进行置换。如果串中包含宏中的形参(如a,b),则将程序语句中的相关参数(可以是常量,变量,或表达式)代替形参。如果宏定义中的字符串中的字符不是参数字符(如上*),则保留,这样就形成了置换的字符串。 带参数的宏与函数有许多相似之处,在调用函数时也是在函数名后的括号内写实参,也要求实参与形参的数目相等,但它们之间还有很大的不同,主要有: 1,函数调用时,先求出实参表达式的值,然后代入形参,而使用带参的宏只是进行简单的字符替换。

汇编语言指令汇总

汇编语言程序设计资料简汇 通用寄存器 8位通用寄存器8个:AL、AH、BL、BH、CL、CH、DL、DH。 16位通用寄存器8个:AX、BX、CX、DX、SI、DI、BP、SP。 AL与AH、BL与BH、CL与CH、DL与DH分别对应于AX、BX、CX和DX的低8位与高8位。专用寄存器 指令指针:IP(16位)。 标志寄存器:没有助记符(FLAGS 16位)。 段寄存器 段寄存器:CS、DS、ES、SS。 内存分段:80x86采用分段内存管理机制,主要包括下列几种类型的段: ?代码段:用来存放程序的指令序列。 ?数据段:用来存放程序的数据。 ?堆栈段:作为堆栈使用的内存区域,用来存放过程返回地址、过程参数等。 物理地址与逻辑地址 ?物理地址:内存单元的实际地址,也就是出现在地址总线上的地址。 ?逻辑地址:或称分段地址。 ?段地址与偏移地址都是16位。 ?系统采用下列方法将逻辑地址自动转换为20位的物理地址: 物理地址= 段地址×16 + 偏移地址 ?每个内存单元具有唯一的物理地址,但可由不同的逻辑地址描述。 与数据有关的寻址方式 立即寻址方式 立即寻址方式所提供的操作数紧跟在操作码的后面,与操作码一起放在指令代码段中。立即数可以是8位数或16位数。如果是16位数,则低位字节存放在低地址中,高位字节存放在高地址中。 例:MOV AL,18 指令执行后,(AL)= 12H 寄存器寻址方式 在寄存器寻址方式中,操作数包含于CPU的内部寄存器之中。这种寻址方式大都用于寄存器之间的数据传输。 例3:MOV AX,BX 如指令执行前(AX)= 6789H,(BX)= 0000H;则指令执行后,(AX)= 0000H,(BX)保持不变。 直接寻址方式 直接寻址方式是操作数地址的16位偏移量直接包含在指令中,和指令操作码一起放在代码段,而操作数则在数据段中。操作数的地址是数据段寄存器DS中的内容左移4位后,加上指令给定的16位地址偏移量。直接寻址方式适合于处理单个数据变量。 寄存器间接寻址方式 在寄存器间接寻址方式中,操作数在存储器中。操作数的有效地址由变址寄存器SI、DI或基址寄存器BX、BP提供。 如果指令中指定的寄存器是BX、SI、DI,则用DS寄存器的内容作为段地址。 如指令中用BP寄存器,则操作数的段地址在SS中,即堆栈段。

第9章 预处理命令

第9章预处理命令 宏定义不是C语句,所以不能在行尾加分号。如果加了分号则会连分号一起进行臵换。 可以用#undef命令终止宏定义的作用域。 对程序中用“”括起来的内容(即字符串内的字符),即使与宏名相同,也不进行臵换。宏定义只做字符替换,不分配内存空间。 宏名不是变量,不分配存储空间,也不能对其进行赋值。 在宏展开时,预处理程序仅对宏名作简单的字符串替换,不作任何检查。 在进行宏定义时,可以引用已定义的宏名 无参宏定义的一般格式: #define 标识符字符串 将这个标识符(名字)称为“宏名”,在用预编译时将宏名替换成字符串的过程称为“宏展开”。#define是宏定义命令。 带参宏定义的一般格式: #define 宏名(形参表)字符串 带参宏的调用和宏展开: 调用格式:宏名(实参表); 宏展开(又称为宏替换)的方法:用宏调用提供的实参直接臵换宏定义中相应的形参,非形参字符保持不变。 定义有参宏时,宏名与左圆括号之间不能留有空格。否则,C编译系统会将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。 有参宏的展开,只是将实参作为字符串,简单地臵换形参字符串,而不做任何语法检查。 为了避免出错,可以在所有形参外,甚至整个字符串外,均加上一对圆括号。 如: #define S(r) 3.14*(r)*(r) 则:area=S(a+b); 展开后为: area=3.14*(a+b)*(a+b); 调用有参函数时,是先求出实参的值,然后再复制一份给形参。而展开有参宏时,只是将实参简单地臵换形参。函数调用是在程序运行时处理的,为形参分配临时的内存单元;而宏展开则是在编译前进行的,在展开时不分配内存单元,不进行值的传递,也没有“返回值”的概念。调用函数只可得到一个返回值,而用宏可以设法得到几个结果。 在有参函数中,形参都是有类型的,所以要求实参的类型与其一致;而在有参宏中,形参和宏名都没有类型,只是一个简单的符号代表,因此,宏定义时,字符串可以是任何类型的数据。 使用宏次数多时,宏展开后源程序变长,因为每展开一次都是程序增长,而函数调用不会使源程序变长。 宏替换不占用运行时间,只占编译时间。而函数调用则占用运行时间(分配单元、保留现场、值传递、返回)。 在程序中如果有带实参的宏,则按#define命令行中指定的字符串从左到右进行臵换。如果字符串中包含宏中的形参,则将程序语句中相应的实参(可以是常量、变量或表达式)代替形参。如果宏定义中的字符串中的字符不是参数字符,则保留。

C#中的预处理指令

3.9.1排除和包含代码 或许最常用的预处理器指令就是用于控制什么时候以及如何包含代码的指令。举个例子来说,要使代码能够同时由C# 2.0和之前的C# 1.2版本编译器进行编译,可以使用一个预处理器指令,在遇到1.2编译器的时候,就排除C# 2.0特有的代码。我们的tic-tac-toe例子和代码清单 3-52对此进行了演示。 代码清单3-52遇到C# 1.x编译器的时候排除C# 2.0代码

在这个例子中,调用了System.Console.Clear()方法,这是只有2.0 CLI才支持的方法。使用#if和#endif预处理器指令,这一行代码就只有在定义了预处理器符号CSHARP2的前提下才会编译。 预处理器指令的另一个应用是适应不同平台之间的差异,比如用WINDOWS和LINUX #if指令将Windows和Linux特有的API包围起来。开发者经常用这些指令来取代多行注释 (/*...*/),因为它们更容易通过定义恰当的符号或者通过一次搜索/替换来移除。预处理器指令最后一个常见的用途是调试。如果用一个#if DEBUG指令将调试代码包围起来,那么在大多数IDE中,都能在最终的发布版本中移除这些代码。IDE默认将DEBUG符号用于调试编译,将RELEASE符号用于发布版本。 为了处理else-if条件,可以在#if指令中使用#elif指令,而不是创建两个完全独立的#if块,如代码清单3-53所示。 代码清单3-53使用#if、#elif和#endif指令 #if LINUX ... #elif WINDOWS ... #endif 输出3-28展示了在使用Mono编译器的前提下,如何实现相同的功能。

#pragma指令用法汇总和解析

#pragma指令用法汇总和解析 一. message 参数。 message 它能够在编译信息输出窗 口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为: #pragma message(“消息文本”) 当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条 指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法 #ifdef _X86 #pragma message(“_X86 macro activated!”) #endif 当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_ X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了 二. 另一个使用得比较多的#pragma参数是code_seg。格式如: #pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] ) 该指令用来指定函数在.obj文件中存放的节,观察OBJ文件可以使用VC自带的dumpbin命令行程序,函数在.obj文件中默认的存放节 为.text节 如果code_seg没有带参数的话,则函数存放在.text节中 push (可选参数) 将一个记录放到内部编译器的堆栈中,可选参数可以为一个标识符或者节名 pop(可选参数) 将一个记录从堆栈顶端弹出,该记录可以为一个标识符或者节名 identifier (可选参数) 当使用push指令时,为压入堆栈的记录指派的一个标识符,当该标识符被删除的时候和其相关的堆栈中的记录将被弹出堆栈 "segment-name" (可选参数) 表示函数存放的节名 例如: //默认情况下,函数被存放在.text节中 void func1() { // stored in .text } //将函数存放在.my_data1节中 #pragma code_seg(".my_data1") void func2() { // stored in my_data1 } //r1为标识符,将函数放入.my_data2节中 #pragma code_seg(push, r1, ".my_data2") void func3() { // stored in my_data2 } int main() { } 三. #pragma once (比较常用) 这是一个比较常用的指令,只要在头文件的最开始加入这条指令就能够保证头文件被编译一次 四. #pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。

c语言预处理命令之条件编译(ifdefelseendifif等)

C语言预处理命令之条件编译(#ifdef,#else,#endif,#if等) 预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。可见预处理过程先于编译器对源代码进行处理。 在C语言中,并没有任何内在的机制来完成如下一些功能:在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码。要完成这些工作,就需要使用预处理程序。尽管在目前绝大多数编译器都包含了预处理程序,但通常认为它们是独立于编译器的。预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行响应的转换。预处理过程还会删除程序中的注释和多余的空白字符。 预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。下面是部分预处理指令: 指令用途 #空指令,无任何效果 #include包含一个源代码文件 #define定义宏 #undef取消已定义的宏 #if如果给定条件为真,则编译下面代码 #ifdef如果宏已经定义,则编译下面代码 #ifndef如果宏没有定义,则编译下面代码 #elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 #endif结束一个#if……#else条件编译块 #error停止编译并显示错误信息 一、文件包含 #include预处理指令的作用是在指令处展开被包含的文件。包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。标准C编译器至少支持八重嵌套包含。

预处理过程不检查在转换单元中是否已经包含了某个文件并阻止对它的多次包含。这样就可以在多次包含同一个头文件时,通过给定编译时的条件来达到不同的效果。例如: #defineAAA #include"t.c" #undefAAA #include"t.c" 为了避免那些只能包含一次的头文件被多次包含,可以在头文件中用编译时条件来进行控制。例如: /*my.h*/ #ifndefMY_H #defineMY_H …… #endif 在程序中包含头文件有两种格式: #include #include"my.h" 第一种方法是用尖括号把头文件括起来。这种格式告诉预处理程序在编译器自带的或外部库的头文件中搜索被包含的头文件。第二种方法是用双引号把头文件括起来。这种格式告诉预处理程序在当前被编译的应用程序的源代码文件中搜索被包含的头文件,如果找不到,再搜索编译器自带的头文件。 采用两种不同包含格式的理由在于,编译器是安装在公共子目录下的,而被编译的应用程序是在它们自己的私有子目录下的。一个应用程序既包含编译器提供的公共头文件,也包含自定义的私有头文件。采用两种不同的包含格式使得编译器能够在很多头文件中区别出一组公共的头文件。

多文件结构和编译预处理命令

多文件结构和编译预处理命令 C++完整的源程序一般由三部分构成:类的定义,类成员的实现,主函数 在较大的项目中,常需要多个源文件(即多个编译单元),c++要求一个类的定义必须在使用该类的编译单元中。因此,把类的定义写在头文件中。 (重点)一个项目至少分三个文件:类定义文件(.h)、类实现文件(.cpp)、类的使用文件(.cpp) (重点)对于复杂的程序:每个类都有单独的类定义和类实现。这样做的好处:可以对不同的文件进行单独编写、编译,最后链接,同时利用类的封 装性,在程序的调试和修改时只对其中某一个类的定义和实现修改,其余保持不动。 预处理指令声明中出现的注释以及一行单独一个#符号的情况在预编译处理过程中都会被 忽略掉。 宏定义在c++中依然使用,但最好的方式是在类型说明语句中用const修饰来取代宏定义。(重点)大型程序中,往往需要使用很多头文件,因此要发现重复包含并不容易。要解决这个问题,我们可以在头文件使用条件编译。(三种方式) 表1是所有预处理指令和意义 指令意义 #define 定义宏 #undef 取消定义宏 #include 包含文件 #ifdef 其后的宏已定义时激活条件编译块 #ifndef 其后的宏未定义时激活条件编译块 #endif 中止条件编译块 #if 其后表达式非零时激活条件编译块 #else 对应#ifdef, #ifndef, 或#if 指令 #elif #else 和#if的结合 #line 改变当前行号或者文件名 #error 输出一条错误信息 #pragma 为编译程序提供非常规的控制流信息 宏定义 #define指令定义宏,宏定义可分为两类:简单宏定义,带参数宏定义。 简单宏定义有如下一般形式: #define 名字替换文本 它指示预处理器将源文件中所有出现名字记号的地方都替换为替换文本,替换文本可以是任

C51几个预编译指令的用法

C51几个预编译指令的用法 标签: 指令用法编译2009-07-31 10:47 预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。可见预处理过程先于编译器对源代码进行处理。 在C语言中,并没有任何内在的机制来完成如下一些功能: 在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码。要完成这些工作,就需要使用预处理程序。尽管在目前绝大多数编译器都包含了预处理程序,但通常认为它们是独立于编译器的。预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行响应的转换。预处理过程还会删除程序中的注释和多余的空白字符。 预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。下面是部分预处理指令: 指令用途 #空指令,无任何效果 #include包含一个源代码文件 #define定义宏 #undef取消已定义的宏 #if如果给定条件为真,则编译下面代码 #ifdef如果宏已经定义,则编译下面代码 #ifndef如果宏没有定义,则编译下面代码

#elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 #endif结束一个#if……#else条件编译块 #error停止编译并显示错误信息 一、文件包含 #include预处理指令的作用是在指令处展开被包含的文件。包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。标准C编译器至少支持八重嵌套包含。 预处理过程不检查在转换单元中是否已经包含了某个文件并阻止对它的多次包含。这样就可以在多次包含同一个头文件时,通过给定编译时的条件来达到不同的效果。例如: #define AAA #include "t.c" #undef AAA #include "t.c" 为了避免那些只能包含一次的头文件被多次包含,可以在头文件中用编译时条件来进行控制。例如: #ifndef MY_H #define MY_H …… #endif 在程序中包含头文件有两种格式: #include #include "my.h"

汇编语言指令集合 吐血整理

8086/8088指令系统记忆表 数据寄存器分为: AH&AL=AX(accumulator):累加寄存器,常用于运算;在乘除等指令中指定用来存放操作数,另外,所有的I/O指令都使用这一寄存器与外界设备传送数据. BH&BL=BX(base):基址寄存器,常用于地址索引; CH&CL=CX(count):计数寄存器,常用于计数;常用于保存计算值,如在移位指令,循环(loop)和串处理指令中用作隐含的计数器. DH&DL=DX(data):数据寄存器,常用于数据传递。他们的特点是,这4个16位的寄存器可以分为高8位:AH,BH,CH,DH.以及低八位:AL,BL,CL,DL。这2组8位寄存器可以分别寻址,并单独使用。 另一组是指针寄存器和变址寄存器,包括: SP(Stack Pointer):堆栈指针,与SS配合使用,可指向目前的堆栈位置; BP(Base Pointer):基址指针寄存器,可用作SS的一个相对基址位置; SI(Source Index):源变址寄存器可用来存放相对于DS段之源变址指针; DI(Destination Index):目的变址寄存器,可用来存放相对于ES段之目的变址指针。 指令指针IP(Instruction Pointer) 标志寄存器FR(Flag Register) OF(overflow flag) DF(direction flag) CF(carrier flag) PF(parity flag) AF(auxiliary flag) ZF(zero flag) SF(sign flag) IF(interrupt flag) TF(trap flag) 段寄存器(Segment Register) 为了运用所有的内存空间,8086设定了四个段寄存器,专门用来保存段地址: CS(Code Segment):代码段寄存器; DS(Data Segment):数据段寄存器; SS(Stack Segment):堆栈段寄存器;

C51几个预编译指令的用法

C51几个预编译指令的用法 标签: 指令用法编译2009-07-31 10:47 预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。可见预处理过程先于编译器对源代码进行处理。 在C语言中,并没有任何内在的机制来完成如下一些功能:在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码。要完成这些工作,就需要使用预处理程序。尽管在目前绝大多数编译器都包含了预处理程序,但通常认为它们是独立于编译器的。预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行响应的转换。预处理过程还会删除程序中的注释和多余的空白字符。 预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。下面是部分预处理指令: 指令用途 #空指令,无任何效果 #include包含一个源代码文件 #define定义宏 #undef取消已定义的宏 #if如果给定条件为真,则编译下面代码 #ifdef如果宏已经定义,则编译下面代码 #ifndef如果宏没有定义,则编译下面代码 #elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 #endif结束一个#if……#else条件编译块 #error停止编译并显示错误信息 一、文件包含 #include预处理指令的作用是在指令处展开被包含的文件。包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。标准C编译器至少支持八重嵌套包含。 预处理过程不检查在转换单元中是否已经包含了某个文件并阻止对它的多次包含。这样就可以在多次包含同一个头文件时,通过给定编译时的条件来达到不同的效果。例如: #define AAA

预编译#define #ifdef #endif用法

预编译#define #ifdef #endif用法 2010-08-05 10:08 最近在看Linux底层代码,发现好多代码里有#define #ifdef #endif,找了个介绍详细的文章,供大家参考: #ifdef的用法 灵活使用#ifdef指示符,我们可以区隔一些与特定头文件、程序库和其他文件版本有关的代码。 代码举例:新建define.cpp文件 #include “iostream.h” int main() { #ifdef DEBUG cout<< “Beginning execution of main()”; #endif return 0; } 运行结果为:Press any key to continue 改写代码如下: #include “iostream.h” #define DEBUG int main() { #ifdef DEBUG cout<< “Beginning execution of main()”; #endif return 0; } 运行结果为:Beginning execution of main() Press any key to continue更一般的情况是,#define 语句是包含在一个特定的头文件中。 比如,新建头文件head.h,在文件中加入代码: #ifndef DEBUG #define DEBUG #endif 而在define.cpp源文件中,代码修改如下: #include “iostream.h” #include “head.h” int main(){ #ifdef DEBUG

cout<< “Beginning execution of main()”; #endif return 0; } 运行结果如下:Beginning execution of main() Press any key to continue 结论:通过使用#ifdef指示符,我们可以区隔一些与特定头文件、程序库和其他文件版本有关的代码。 “#ifdef”的运用magicchip 发表于 2007-8-16 9:10:00 “#ifdef”在C++中运用 常常在建立header file的时候需要先写一段文字 比如下面这段话 #ifdef HEADERFILENAME_H #define HEADERFILENAME_H class ClassName { … }; #endif 那段#ifdef有必要写吗? 其实有些无写都不会影响代码的Runtime 不过对运行速度和文件大小有一定的影响 大概很多人很喜欢用#i nclude来抓取文件吧 炎也很喜欢 总是在main.cpp中include一次 然后又在classfile.cpp中又使用一次

变量的存储类型与预编译命令

实验十二变量的存储类型与与编译命令(上机练习)1、输入下面的程序并运行,分析为什么会得到此结果。 #include #define PI 3.1415926 #define S(r) PI*r*r float S1(int r) { return(PI*r*r); } void main() { printf("%f\n",S(2)); printf("%f\n",S(1+1)); printf("%f\n",S1(2)); printf("%f\n",S(1+1)); } 具体要求: ①分析程序的运行结果。 ②认真比较函数和宏定义的不同之处。 2.以下程序的输出结果是()。 A.15 B.100 C.10 D.150 #include #define MIN(x,y) (x)<(y)?(x):(y) main() { int i,j,k;i=10;j=15;k=10*MIN(i,j); printf("%d\n",k);} 3.以下程序中的for循环执行的次数是()。 A.5 B.6 C.8 D.9 #define N 2 #define M N+1 #define NUM (M+1)*M/2 main() { int i;for(i=1;i<=NUM;i++); printf(“%d\n”,i);} 4.以下程序的输出结果是()。

A.11 B.12 C.13 D.15 #include “stdio.h” #define FUDGF(y) 2.84+y #define PR(a) printf(“%d”,(int)(a)) #define PRINT1(a) PR(a);putchar(‘\n’) main() { int x=2;PRINT1(FUDGF(5)*x); } 5.以下叙述正确的是()。 A.用#include包含的头文件的后缀不可以是“.a” B.若一些源程序中包含某个头文件;当该头文件有错时,只需对该头文件进行修改,包含此头文件所有源程序不必重新进行编译 C.宏命令可以看做是一行C语句 D.C编译中的预处理是在编译之前进行的。 6.以下有关宏替换的叙述不正确的是()。 A.宏替换不占用运行时间 B.宏名无类型 C.宏替换只是字符替换 D.宏名必须用大写字母表示7.宏定义#define G 9.8中的宏名G代替() A.一个单精度实数 B.一个双精度实数C.一个字符串 D.不确定类型的数8.若有宏定义: #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.以下程序的运行结果是()。 #define DOUBLE(r) r*r main( ) { int y1=1,y2=2,t; t=DOUBLE(y1+y2); printf(“%d\n”,t);} A.11 B.10 C.5 D.9 10.下列程序段的输出结果是()

汇编语言指令分类详解

3.1 8086/8088寻址方式 计算机中的指令由操作码字段和操作数字段组成。 操作码:指计算机所要执行的操作,或称为指出操作类型,是一种助记符。 操作数:指在指令执行操作的过程中所需要的操作数。该字段除可以是操作数本身外,也可以是操作数地址或是地址的一部分,还可以是指向操作数地址的指针或其它有关操作数的信息。 寻址方式就是指令中用于说明操作数所在地址的方法,或者说是寻找操作数有效地址的方法。8086/8088的基本寻址方式有六种。 1.立即寻址 所提供的操作数直接包含在指令中。它紧跟在 操作码的后面,与操作码一起放在代码段区域中。 如图所示。 例如:MOV AX,3000H 立即数可以是8位的,也可以是16位的。若 是16位的,则存储时低位在前,高位在后。 立即寻址主要用来给寄存器或存储器赋初值。 2.直接寻址 操作数地址的16位偏移量直接包含在指令中。它与操作码—起存放在代码段区域,操作数一般在数据段区域中,它的地址为数据段寄存器DS加上这16位地址偏移量。如图2-2所示。 例如:MOV AX,DS:[2000H];

图2-2 (对DS来讲可以省略成MOV AX,[2000H],系统默认为数据段)这种寻址方法是以数据段的地址为基础,可在多达64KB的范围内寻找操作数。 8086/8088中允许段超越,即还允许操作数在以代码段、堆栈段或附加段为基准的区域中。此时只要在指令中指明是段超越的,则16位地址偏移量可以与CS或SS或ES相加,作为操作数的地址。 MOV AX,[2000H] ;数据段 MOV BX,ES:[3000H] ;段超越,操作数在附加段 即绝对地址=(ES)*16+3000H 3.寄存器寻址 操作数包含在CPU的内部寄存器中,如寄存器AX、BX、CX、DX等。 例如:MOV DS,AX MOV AL,BH 4.寄存器间接寻址 操作数是在存储器中,但是,操作数地址的16位偏移量包含在以下四个寄存器SI、DI、BP、BX之一中。可以 分成两种情况: (1)以SI、DI、BX间接寻址,则 通常操作数在现行数据段区域 中,即数据段寄存器(DS)*16 加上SI、DI、BX中的16位偏移 量,为操作数的地址, 例如:MOV AX,[SI] 操作数地址是:(DS)*16+(SI) (2)以寄存器BP间接寻址,则操作数在堆栈段区域中。即堆栈段寄存器(SS)*16与BP的内容相加作为操作数的地址, 例如:MOV AX,[BP] 操作数地址是:(SS)*16+(BP)若在指令中规定是段超越的,则BP的内容也可以与其它的段寄存器相加,形成操作数地址。 例如:MOV AX,DS:[BP] 操作数地址是:(DS)*16+(BP)5.变址寻址 由指定的寄存器内容,加上指令中给出的8位或16位偏移量(当然要由一个

相关主题
文本预览
相关文档 最新文档