(该内容是在观看了孙鑫老师的MFC教程后,觉得有必要记录的)

  在学习c++时,我们经常会用到#include,#define两种预编译符,很简单,前者是一种引用文件的方法,后者是宏定义的方法。

  引用文件,在你写的源代码前可以引用其他文件,将其他文件中的内容直接进行使用。常见的比如使用数学函数要添加#include <cmath>。

  宏定义,是一种下新定义的方法,给出一个新的“名词”表示某个值,通俗地讲,宏定义就是给某个值起个外号,我们可以通过外号直接使用该值。比如说#define pi 3.1415926,我们可以说新定义了一个pi,令它为3.1415926,亦可以说给3.1415926这个值起了个新名字。

  在学习MFC时,总可以遇见其他的许多预编译符#error,#ifdef,#endif等等,虽然它们的存在似乎不影响我们写程序,但没有了它们,编译运行程序可能将困难重重。

一、预编译符在编译过程中的哪一步

  

  (图片源自孙鑫老师的MFC视频教程)

  预处理发生在编译之前,主要作用就是包含文件,宏替换,条件编译,布局控制。

  我认为预编译符就是一种得到完整的需编译的无重复的源代码的方法。以下为我自己的想法:包含文件,利用#include包含各种头文件,使代码完整;宏替换,一种简化、增加可读性的方法;条件控制,有选择的选取编译片段和剔除重复定义的方法;布局控制,还没理解。。。。

  百度百科

文件包含:#include 是一种最为常见的预处理,主要是做为文件的引用组合源程序正文。
条件编译:#if,#ifndef,#ifdef,#endif,#undef等也是比较常见的预处理,主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含的功能。
布局控制:#progma,这也是我们应用预处理的一个重要方面,主要功能是为编译程序提供非常规的控制流信息。
宏替换: #define,这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字符串的拼接等各种功能。
二、预编译符总结
  (来自帮助手册)

  # 和 ## 操作符是和#define宏使用的. 使用# 使在#后的首个参数返回为一个带引号的字符串. 例如, 命令

#define to_string( s ) # s
//将会使编译器把以下命令
cout << to_string( Hello World! ) << endl;
//理解为
cout << "Hello World!" << endl;

  使用##连结##前后的内容. 例如, 命令

#define concatenate( x, y ) x ## y int xy = 10;
//将会使编译器把
cout << concatenate( x, y ) << endl;
//解释为
cout << xy << endl;
//理所当然,将会在标准输出处显示'10'.

  #define命令用于把指定的字符串替换文件中的宏名称 . 也就是说, #define使编译器把文件中每一个macro-name替换为replacement-string. 替换的字符串结束于行末. 这里是一个经典的#define应用 (至少是在C中):

#define TRUE 1
#define FALSE 0
int done = 0;
while( done != TRUE ) { ... }

  #define命令的另外一个功能就是替换参数,使它 假冒创建函数一样使用. 如下的代码:

//对参数x取绝对值的宏定义
#define absolute_value( x ) ( ((x) < 0) ? -(x) : (x) )
int x = -1;
while( absolute_value( x ) ) { ... }

  #error命令可以简单的使编译器在发生错误时停止. 当遇到一个#error时,编译器会自动输出行号而无论message的内容. 本命令大多是用于调试.

  #if, #ifdef, #ifndef, #else, #elif, #endif这些命令让编译器进行简单的逻辑控制. 当一个文件被编译时, 你可以使用这些命令使某些行保留或者是去处.

  #if expression  如果表达式(expression)的值是"真"(true),那么紧随该命令的代码将会被编译.

  #ifdef macro  如果"macro"已经在一个#define声明中定义了, 那么紧随该命令的代码将会被编译.

  #ifndef macro  如果"macro"未在一个#define声明中定义, 那么紧随命令的代码将会被编译.

  一些小边注: 命令#elif是"elseif"的一种缩写,并且他可以想你所意愿的一样工作. 你也可以在一个#if后插入一个"defined"或者"!defined"以获得更多的功能.这里是一部分例子:

//判断是否宏定义过DEBUG,并依情况输出
#ifdef DEBUG
cout << "This is the test version, i=" << i << endl;
#else
cout << "This is the production version!" << endl;
#endif

  #include <iostream>本命令包含一个文件并在当前位置插入. 两种语法的主要不同之处是在于,如果filename括在尖括号中,那么编译器不知道如何搜索它. 如果它括在引号中, 那么编译器可以简单的搜索到文件. 两种搜索的方式是由编译器决定的,一般尖括号意味着在标准库目录中搜索, 引号就表示在当前目录中搜索. The spiffy new 整洁的新C++ #include目录不需要直接映射到filenames, 至少对于标准库是这样. 这就是你有时能够成功编译以下命令的原因

  #line命令是用于更改 __LINE__ 和 __FILE__变量的值. 文件名是可选的. __LINE__ 和 __FILE__ 变量描述被读取的当前文件和行. 命令

//更改行号为10,当前文件改为"main.cpp".
#line 10 "main.cpp" 

  #pragma命令可以让编程者让编译器执行某些事. 因为#pragma命令的执行很特殊,不同的编译器使用有所不同. 一个选项可以跟踪程序的执行.

  #undef命令取消一个先前已定义的宏变量, 譬如一个用#define定义的变量.

预定义变量

  下列参数在不同的编译器可能会有所不同, 但是一般是可用的:

  • __LINE__ 和 __FILE__ 变量表示正在处理的当前行和当前文件.

  • __DATE__ 变量表示当前日期,格式为month/day/year(月/日/年).

  • __TIME__ 变量描述当前的时间,格式为hour:minute:second(时:分:秒).

  • _cplusplus 变量只在编译一个C++程序时定义.

  • __STDC__ 变量在编译一个C程序时定义,编译C++时也有可能定义.

末了,#ifdef等的用途

  在MFC中,类不可以重复定义,而#include包含头文件会重复编译,于是可能出现如下,出错情形

(Animal.h中)
class Animal{...}(Fish.h中)
#include "Animal.h"
class Fish:public Animal{...}(main.cpp中)
#include “Animal.h"
#include "Fish.h"
void main(){...}    

  编译代码时,首先从main.cpp中开始,由预编译符#include ”Animal.h“第一次编译Animal类。接着由#include ”Fish.h“第二次编译Animal类,此时重复定义。再然后编译Fish类和main函数。

  正确情形

(Animal.h中)
#ifndef  XXXX
#define XXXX        //定义,值并不是必需的,此处的定义无使用意义,不需要值
class Animal{...}
#endif(Fish.h中)
#include "Animal.h"
class Fish:public Animal{...}(main.cpp中)
#include “Animal.h"
#include "Fish.h"
void main(){...}    

  同样的内容,在第一次编译Animal类时,由于没有宏定义过XXXX,所以编译其中内容,第二次由于宏定义过XXXX,不再进行编译,于是不存在错误。

转载于:https://www.cnblogs.com/aLandon/p/7246731.html

关于c++预编译符的使用相关推荐

  1. mybatis以及预编译如何防止SQL注入

    SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者).[摘自] SQL injection - Wikipedia SQL ...

  2. mybatis深入理解(一)之 # 与 $ 区别以及 sql 预编译

    mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = ...

  3. C++封装常用对象和对头文件以及预编译机制的探索

    在C++实际开发中,难免会使用到一些你极为常用的算法(比如笔者经常使用的多线程技术),实现这些算法的类或是全局函数或是命名空间等等经常都要被使用多次,你会有哪些办法来使用呢?笔者有4个办法. 第一个方 ...

  4. PreparedStatement预编译的sql执行对象

    一.预编译,防sql注入 其中,设置参数值占位符索引从1开始:在由sql 连接对象创建 sql执行对象时候传入参数sql语句,在执行对象在执行方法时候就不用再传入sql语句: 数据库索引一般是从1开始 ...

  5. 课程 预编译框架,开发高性能应用 - 微软技术暨生态大会 2018

    微软技术暨生态大会(Tech Summit),2018 年在上海世博中心召开.这是最后一次的 Tech Summit 了:明年开始,中国大陆地区就要和其他国家和地区一样,进行全球 Ignite Tou ...

  6. c语言万能预编译,Objective-C学习笔记

    import 指令(将文件的内容在预编译的时候拷贝到写指令的地方) import Foundation 框架 NSLog 函数 NSLog(@"Hello, World!"); N ...

  7. java安全(二):JDBC|sql注入|预编译

    给个关注?宝儿! 给个关注?宝儿! 给个关注?宝儿! 关注公众号:b1gpig信息安全,文章推送不错过 1 JDBC基础 JDBC(Java Database Connectivity)是Java提供 ...

  8. 5单个编译总会编译全部_JDBC【5】 JDBC预编译和拼接Sql对比

    在jdbc中,有三种方式执行sql,分别是使用Statement(sql拼接),PreparedStatement(预编译),还有一种CallableStatement(存储过程),在这里我就不介绍C ...

  9. C语言的预编译,程序员必须懂的知识!【预编译指令】【预编译过程】

    由"源代码"到"可执行文件"的过程包括四个步骤:预编译.编译.汇编.链接.所以,首先就应该清楚的首要问题就是:预编译只是对程序的文本起作用,换句话说就是,预编译 ...

最新文章

  1. 四边形内接于圆定理_【初中数学】几何证明靠定理,所有证明定理都在这里
  2. 北京市常用和便民电话
  3. pointnet与pointnet++
  4. MySQL视图附带例子详解(小白都能懂哦)
  5. linux封装函数,libc库和封装函数 | 求索阁
  6. [css] 你对伪类了解多少?分为几大类?
  7. 队列处理高并发_高并发场景下缓存处理的一些思路
  8. Flask框架——路由和视图
  9. Harris角点检测和Shi-Tomasi角点检测
  10. python 脚本编码_Python-我如何编码我的Python脚本
  11. Nginx源码分析 - Event事件篇 - Event模块和配置的初始化(16)
  12. 二重积分x^2+y^2_计算二重积分∫∫y^2dxdy,其中D是由圆周x^2+y^2=1所围成的闭区域...
  13. Hitool网口烧写失败问题
  14. linux fedora14 u盘运行,用U盘安装FEDORA14后必须从U盘启动,从硬盘无法启动
  15. ubuntu软件的卸载
  16. 如何配置静态路由使三台PC机互联
  17. 自建团队app公司外包免编程app打包平台优缺点分析
  18. 使用request和re爬取豆瓣250排行榜信息
  19. 闪光法测量高导热碳化硅(4H-SiC、6H-SiC)圆晶中存在的问题
  20. ss使用ipv6地址

热门文章

  1. 城市大脑,是工程问题,但首先是基础科学问题
  2. DARPA 2020财年研发预算 人工智能应用研究投资急剧增长
  3. 盘点丨毕业年薪34万,高校人工智能研究哪家强?
  4. CES现场低调的主线,近在咫尺的5G商业化 | CES2018技术趋势
  5. 两个黑箱问题 ——深度神经网络和脑神经网络
  6. 学术界盛事揭幕:一图解读跨越百余年的诺贝尔奖
  7. 不发项目奖金,程序员怒删代码,被判 5 个月!
  8. 从小护士到微软中国总经理,逆风飞扬的“打工皇后”吴士宏的传奇人生
  9. 漫画:什么是 “并查集” ?
  10. Maven学习总结(七)——eclipse中使用Maven创建Web项目