后台开发:核心技术与应用实践1.7 预处理

1.7 预处理

C++提供的预处理功能主要有以下4种:宏定义、文件包含、条件编译和布局控制。文件包含在前面已描述过,下面重点描述宏定义、条件编译和布局控制,其中又着重讲述常用宏定义命令、do…while(0)的妙用、条件编译及extern"C"块的应用知识。

1.?常用宏定义命令

#def?ine命令是一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义。

简单的宏定义的声明格式如下所示:

#define 宏名 字符串

例:#def?ine PI 3.1415926

带参数的宏定义的声明格式如下所示:

#define 宏 (参数表列) 宏

例:#def?ine A(x) x

使用宏定义中,要注意以下问题。

(1)在简单宏定义的使用中,当替换文本所表示的字符串是一个表达式时,需要加上括号,否则容易引起误解和误用。

【例1.17】 简单宏定义不加括号容易引起误用。

#include<iostream>

#define N 2+9

using namespace
std;

int main(){

    int a=N*N;

    cout<<a<<endl;

    return 0;

}

程序的执行结果是:

29

例1.17中就出现了问题:在此程序中存在着宏定义命令,宏N代表的字符串是2+9,在程序中有对宏N的使用,一般同学在读该程序时,容易产生的问题是先求解N为2+9=11,然后在程序中计算a时使用乘法,即N*N=11*11=121,其实该题的结果为29,为什么结果有这么大的偏差?因为宏展开是在预处理阶段完成的,这个阶段把替换文本只是看作一个字符串,并不会有任何的计算发生,在展开时是在宏N出现的地方只是简单地使用串2+9来代替N,并不会增添任何的符号,所以对该程序展开后的结果是a=2+9*2+9,计算后结果为29。要程序如之前想要的结果,只需要写成#def?ine
N (2+9),即加上括号就行。

(2)类似地,在带参数的宏定义的使用中,也容易引起误解。例如当需要使用宏替换来求任何数的平方,这时就需要使用参数,以便在程序中用实际参数来替换宏定义中的参数。初学者容易写成如例1.18中的形式。

【例1.18】 带参数的宏定义不加括号容易引起误用。

#include<iostream>

using namespace
std;

#define area(x)
x*x

int main()

{

    int y = area(2+2);

    cout<<y<<endl;

    return 0;

}

程序的执行结果是:

8

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

要想能够真正使用好宏定义,在读别人的程序时,一定要记住先将程序中对宏的使用全部替换成它所代表的字符串,不要自作主张地添加任何其他符号,完全展开后再进行相应的计算,就不会求错运行结果。

如果是自己在编程时使用宏替换,则在使用简单宏定义时,当字符串中不只一个符号时,加上括号表现出优先级,如果是带参数的宏定义,则要给宏体中的每个参数加上括号,并在整个宏体上再加一个括号。

2.?do...while(0)的妙用

大家都知道,do{…}while(condition)可以表示循环,但你有没有遇到在一些宏定义中可以不用循环的地方,也用到了do{…}while,比如有这样的宏:

#define Foo(x)
do{\

    statement one;\

    statement two;\

}while(0)          // 这里没有分号

粗看会觉得很奇怪,既然循环里面只执行了一次,那要这个看似多余的do...while(0)有什么意义呢?再来看这样的宏:

#define Foo(x)
{\

    statement one;\

    statement two;\

}

这两个看似一样的宏,其实是不一样的。前者定义的宏是一个非复合语句,而后者却是一个复合语句。假如有这样的使用场景:

if(conditon)

    Foo(x);

else

    ...;

因为宏在预处理的时候会直接被展开,采用第2种写法,会变成:

if(condition)

    statement one;

    statement two;

else

    ...///

这样会导致else语句孤立而出现编译错误。加了do{...}while(0),就使得宏展开后,仍然保留初始的语义,从而保证程序的正确性。

3.?条件编译

一般情况下,源程序中所有行的语句都参加编译。但是有时程序员希望其中一部分内容只在满足一定条件时才进行编译,也就是对一部分内容指定编译的条件,这就用到了“条件编译”。

条件编译命令最常见的形式为:

#ifdef 标识符

    程序段1

#else

    程序段2

#endif

它的作用是:当标识符已经被定义过(一般是用#def?ine命令定义),则对程序段1进行编译,否则编译程序段2。其中#else部分也可以没有,即:

#ifdef 标识符

    程序段1

#endif

下面这样的形式则是当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。可以事先给定一定条件,使程序在不同的条件下执行不同的功能。

#if 表达式

    程序段1

#else

    程序段2

#endif

这里的“程序段”可以是语句组,也可以是命令行。

有时候程序中的某些调试代码,只需要在调试的时候被编译,而不希望在程序的正式发行版中被编译,你可能会看到类似例1.19这样的代码段。

【例1.19】 调试代码巧用条件编译。

#include<iostream>

using namespace
std;

#define _DEBUG_

int main(){

    int x=10;

    #ifdef _DEBUG_

        cout<<"File:"<<
__FILE__<<",Line:"<<
__LINE__<<",x:"<<x<<endl;

    #else

        printf("x = %d\n", x);

        cout<<x<<endl;

    #endif

    return 0;

}

程序的运行结果是:

File:test.cpp,Line:7,x:10

当_DEBUG_没有被定义的时候,仅编译#else与#endif之间的代码;当定义了_DEBUG_符号之后,则会编译#ifdef与#else之间的代码。要想定义一个符号很简单,只需要在文件头部加上像这样的一条语句:

 #define _DEBUG_

用#def?ine命令的目的不在于用_DEBUG_代表一个字符串,而只是表示已定义过_DEBUG_,因此_DEBUG_后面写什么字符串都无所谓,甚至可以不写字符串。

4.?extern
"C"块的应用

经常能在C与C++混编的程序中看到这样的语句:

#ifdef
__cplusplus

    extern "C" {

#endif

    ...

#ifdef __cplusplus

 }

#endif

其中,__cplusplus是C++的预定义宏,表示当前开发环境是C++。在C++语言中,为了支持重载机制,在编译生成的汇编代码中,会对函数名字进行一些处理(通常称为函数名字改编),如加入函数的参数类型或返回类型等,而在C语言中,只是简单的函数名字而已,并不加入其他信息,如下所示:

int func(int
demo);

int func(double
demo);

C语言无法区分上面两个函数的不同,因为C编译器产生的函数名都是_func,而C++编译器产生的名字则可能是_func_Fi和_func_Fd,这样就很好地把函数区别开了。

所以,在C/C++混合编程的环境下,extern
"C"块的作用就是告诉C++编译器这段代码要按C标准编译,以尽可能地保持C++与C的兼容性。例1.20说明了__cplusplus的使用方法。

【例1.20】 __cplusplus的使用方法。

#include<stdio.h>

int main() {

    #define TO_LITERAL(text) TO_LITERAL_(text)

    #define TO_LITERAL_(text) #text

    #ifndef __cplusplus

    /* this translation unit is being treated
as a C one */

        printf("a C program\n");

    #else

       /*this translation unit is being treated
as a C++ one*/

        printf("a C++ program\n__cplusplus
expands to \""

          TO_LITERAL(__cplusplus)
"\"\n");

    #endif

    return 0;

}

程序的执行结果是:

a C++ program

__cplusplus
expands to "1"

例1.20中程序的意思是:如果没有定义__cplusplus,那么当前源代码就会被当作C源代码处理;如果定义了__cplusplus,那么当前源代码会被当中C++源代码处理,并且输出__cplusplus宏被展开后的字符串。

时间: 2024-10-29 21:45:36

后台开发:核心技术与应用实践1.7 预处理的相关文章

后台开发:核心技术与应用实践

后台开发:核心技术与应用实践 徐晓鑫 著 图书在版编目(CIP)数据 后台开发:核心技术与应用实践 / 徐晓鑫著. -北京:机械工业出版社,2016.8 ISBN 978-7-111-54339-8 I. 后- II. 徐- III. 网络-开发 IV. TP393.092 中国版本图书馆CIP数据核字(2016)第167884号 后台开发:核心技术与应用实践 出版发行:机械工业出版社(北京市西城区百万庄大街22号 邮政编码:100037) 责任编辑:李 艺 责任校对:董纪丽 印 刷: 版 次:

后台开发:核心技术与应用实践导读

后台开发:核心技术与应用实践 徐晓鑫 著 图书在版编目(CIP)数据 后台开发:核心技术与应用实践 / 徐晓鑫著. -北京:机械工业出版社,2016.8 ISBN 978-7-111-54339-8 I. 后- II. 徐- III. 网络-开发 IV. TP393.092 中国版本图书馆CIP数据核字(2016)第167884号 后台开发:核心技术与应用实践 出版发行:机械工业出版社(北京市西城区百万庄大街22号 邮政编码:100037) 责任编辑:李 艺 责任校对:董纪丽 印 刷: 版 次:

Android开发:优化ListView实践解析

 在看了一些vogella的文章之后,发现关于android listview性能优化这一段很有意思,于是实践了一下,经过优化,性能确实提升不少! 先看看优化前和优化后的比较: 优化前的log截图: 开发:优化ListView实践解析-"> 优化后的log截图: 并且,在不停滚动ListView的过程中,优化之前会出现ANR现象,在AVD上特别容易复现: 然后,优化后显得很流畅,附上对于的log截图: 下面附上相关代码分析: ListView中的每一个Item由一个ImageView 和一

android-Android后台开发相关书籍资料

问题描述 Android后台开发相关书籍资料 Android APP后台搭建过程,如何使用开发语言实现,请推荐相关书籍,谢谢. 解决方案 <第一行代码> <疯狂Android讲义> <Android群英传> <Android开发艺术探究> 解决方案二: 后台开发相关书籍Android各层开发推荐书籍及资料(转)Android相关开发资料汇总 解决方案三: http://www.jikexueyuan.com/course/2208.html 解决方案四: 深

mysql-app后台开发,如何做好安全性?

问题描述 app后台开发,如何做好安全性? app后台开发一枚,现在要对整个项目做一些安全性,比如拦截非法请求,sql注入什么的 后台开发技术:spring + mybatis + mysql 求一些思路.麻烦了. 解决方案 比如检查密码的sql语句一定要处理特殊字符? = ' 等等,最后还是要写file.将你所设想到的情况全写下来.后期安全还有问题再添加. 解决方案二: 最好安卓请求时要带上自己的用户信息 解决方案三: 接口的话,多加一些加密处理了,然后在登录的时候带token,再就是服务器的

linux 后台开发-后台开发需要些什么东西

问题描述 后台开发需要些什么东西 我是一名大三的学生,想做后台开发,但是又不知道该从哪方面做起.目前Linux c/c++都学的不错,数据结构也学过,SQL.socket网络通信也学过,TCP/IP协议也有一定了解,然后,下一步不知道该干嘛了,我觉得自己学的这些东西比较基础,都不知道怎么怎么能把它们应用实际.望各位大牛能指点迷津 解决方案 这看你想做哪方面的了.linux服务器开发维护,数据库开发维护,网站后台web开发(javawebphp.net等) 解决方案二: 你所学的仅仅是基础,多看看

Linux 后台开发工作中常用的开源库

后台开发,语言主要是 c 和 c++ , 这里简单罗列一下工作中用的很频繁的那些开源软件 1. OpenSSL openssl OpenSSL 是一个安全套接字层密码库,囊括主要的密码算法.常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用. 下载地址: https://www.openssl.org/source/ 2.TinyXML tinyxml 简单,高效,灵活的一套操作 XML 文件的开源库. 下载地址: http://www.grinninglizar

javascript-App后台开发用哪种语言

问题描述 App后台开发用哪种语言 我想问开发一个APP的后台,想实现的功能有实时聊天,图片,视频的上传和下载等, 用java写好,还是其他的好 解决方案 java.php.python.c++.....很多,建议使用java,轮子多,效率高,也可以用python,简单,效率也不低 解决方案二: 后台有很多选择,如果你熟悉android,那么当然选java,那是你熟悉的语言,如果你熟悉js,可以用node.js,其它的语言还有C# VB PHP Ruby Python Go ... 解决方案三:

ios-iOS APP后台开发技术问题请教

问题描述 iOS APP后台开发技术问题请教 如果要搭建一个IOS应用(假设用户量为几十万)的后台服务器,现在比较靠谱的后台架构是怎样的? (先说说我现在的理解,之前做过一些Android开发的事情,当时后台是写了一个简单的Http服务器(利用Netty实现),然后前端和后台的数据交换采用json实现,至于服务器硬件的配置等等没有过考虑.所以我现在可以认识到的后台,就是HTTP请求+Json返回数据.) 想请教一下各位:从服务器硬件到操作系统再到后台技术框架,有没有一个比较系统.成熟.可靠的模式

Java后台开发精选知识图谱

引言: 学习一个新的技术时,其实不在于跟着某个教程敲出了几行.几百行代码,这样你最多只能知其然而不知其所以然,进步缓慢且深度有限,最重要的是一开始就对整个学习路线有宏观.简洁的认识,确定大的学习方向,这样才能事半功倍. 我们经常会遇到这样的情况: 一开始学习一门新技术的时候,面对着很多很多陌生的名词,无从下手,一度想要放弃. 本文首先会给出关于java后台开发和前端适配的一些建议学习路线,接着简单解释一些应用到的高频技术,帮助大家理解和学习,算是一个入门篇. Java后台开发知识一览 1.后端