首页 > 编程语言 > 详细

C++预处理

时间:2021-07-26 23:00:35      阅读:40      评论:0      收藏:0      [点我收藏+]

预处理指令

 

一,预处理

1.1.定义

在编译器正式编译源代码之前,会先进行预处理操作。

C++中,所有的预处理指令都由“#”开头。预处理指令结尾不写分号。

1.2.预处理了什么

把注释去掉,把include的头文件引用进来,define的内容进行替换。

1.3.有哪些预处理指令?

#include

#define

#if

#else

#endif

 

二,include

用法1:#include<文件>

用法2:#include“文件”

用法1用于包含标准库所在目录的头文件,用法2表示包含当前目录下的头文件。

说白了,include就是把另外一个文件的所有内容不管三七二十一放进来就是了。

问题:如果想要引用其他文件夹的头文件?

回答:加上目录不就行了。例如bits/stdc++.h,表示在标准库目录下bits目录下的stdc++.h文件。

问题:引用的文件有类型的要求吗?(必须是.h?)

答案:没有要求,只要内容是文本就行(例如txt)。#include<iostream>iostream就是一个没有扩展名的文件。

 

三,define指令

define指令用于定义宏。

3.1.无参数宏

define在这类场合下,可以起到常量定义的效果。

 

#include<bits/stdc++.h>

#define PI 3.14159

using namespace std;

int main(){

cout<<PI<<endl;

}

 

在运行第5行的时候,预处理器会把PI替换为3.14159

3.2.带参数宏

格式:#define 宏名(参数列表) 内容

我们知道,直接定义函数,函数由于要有调用——返回的过程,执行速度相对较慢。如果使用宏,速度会有提高。

示例:swap函数。

 

#include<bits/stdc++.h>

#define Swap(a,b) {int temp=a;a=b;b=temp;}

using namespace std;

int main(){

int m=2,n=3;

Swap(m,n);

cout<<m<<n;

}

 

程序会把swapmn)按照define替换为:int temp=m;m=n;n=temp;

 

3.3.参数宏的局限性

例如:计算平方。

#include<bits/stdc++.h>

#define func(a) a*a

using namespace std;

int main(){

cout<<func(2+3);

}

 

输出:11

 

我们观察一下,func(2+3)替换成了:

2+3*2+32+3没有打上括号,结果等于2+6+3=11

因此,我们需要进行修改。

 

#include<bits/stdc++.h>

#define func(a) ((a)*(a))

using namespace std;

int main(){

cout<<func(2+3);

}

 

这样就解决了。但是还有问题,这是避免不了的。

#include<bits/stdc++.h>

#define func(a) ((a)*(a))

using namespace std;

int main(){

    int n=4;

cout<<func(++n);

}

输出:36

这是因为,++n执行了两次。这种情况,避免重复计算,这类的函数应该写成inline替代。

 

三,条件编译

3.1.条件编译

有几个指令可以用来有选择地对部分程序源代码进行编译。这个过程被称为条件编译。条件预处理的结构与if 选择结构很像。请看下面这段的代码:

 

#ifdef DEBUG

cout<<a<<endl;

#endif

 

这句话说明,如果定义了debug宏,那么执行下面的语句。在调试程序的时候,可以这样编译,不调试的时候只要取消定义debug即可。

 

3.2.示例:重复引用头文件问题

比较常见的例子是重复引用头文件的问题。例如,程序中有:

#include<a.h>

#include<b.h>

不巧的是,b.h的开头也有个include<a.h>,这样把a.h引用两遍。如果变量或者函数定义两遍会出错,因此,头文件一般都有一个避免重复引用的机制,如下:

 

#ifndef _a_h

#define _a_h 1

头文件内容

#endif

 

第一次引用时候,没有定义a_h,因此把头文件引用进来。第二次,已经定义了a_h,就跳过整个头文件。这样的设计就可以保护头文件不被重复引用。自己编写自己的头文件时,也必须要注意这一点。

 

四,###运算符

4.1.token

C++token包括关键字,标识符,括号,符号等。

我们所有的define替换都是对token进行替换的(并非按字符替换)。例如:#define f 1

那么:cout<<ff<<endl;

不会把ff替换为11。(因为ff是一个token

 

4.2.#运算符

#运算符用于把一个token转换为字符串。

 

举例:assert模拟。assertx)的作用是检测x是否为0

例如,assert(a+2),如果a=-2,那么a+2等于0,输出:“assertion a+2 failed

assert(x==3),如果x不等于3,输出“assertion x==3 failed”

分析:如果直接用普通函数assert(x),只能判断x是否为0,无法输出表达式。(因为输出的内容有assertion 表达式 failed,用普通变量只能输出assertion 0 failed

因此,使用#运算符先转换为字符串,再输出。

程序:

#define Assert(expression) Assert_Sub(#expression,expression)

void Assert_Sub(char *s,int n){

  if(n==0)cout<<“assertion”<<s<<“failed”;

}

 

#expression表示把expression这个token转换为字符串。因此,我们可以完美解决这个问题。

 

4.3.##运算符

##运算符用于把两个token连接为一个token

举例:

#include<bits/stdc++.h>

#define cat(a,b) a##b

using namespace std;

int main(){

    int ab=3;

cout<<cat(a,b);

}

ab连接起来就是标识符ab,也就是变量。

C++预处理

原文:https://www.cnblogs.com/jisuanjizhishizatan/p/15062787.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!