1)预处理
根据已放置在文件中的预处理指令来修改源文件的 内容
预处理器会分析\执行所有的预处理器指令,然后删除他们,得到一个仅包含C++语句的转换单元
预处理指令以#号开头
常用的预处理指令:
#include 包含头文件
#if 条件
#else 否则
#elif 否则如果
#endif 结束条件
#ifdef 或 #if defined 如果定义了一个符号, 就执行操作
#ifndef 或 #if !defined 如果没有定义一个符号, 就指执行操作
#define 定义一个符号
#undef 删除一个符号
#line 重新定义当前行号和文件名
#error 输出编译错误 消息, 停止编译
#pragma 提供 机器专用的特性,同时保证与C++的完全兼容
2)#include 在 程序中包含头文件
头文件通常以.h结尾,其 内容可使用#include预处理器指令包含到 程序中
头文件中一般包含: 函数原型与全局变量
形式常有下面两种
#include <iostream>
#include "myheader.h"
前者<>用来引用标准库头文件,后者""常用来引用自定义的头文件
前者<>编译器只搜索包含标准库头文件的默认 目录,后者首先搜索正在编译的源文件所在的 目录,找不到时再搜索包含标准库头文件的默认 目录.
如果把头文件放在其他 目录下,为了查找到它,必须在双引号中指定从源文件到头文件的完整路径
3)#define 定义符号、宏
1>符号
#define PI 3.1415925 定义符号PI为3.1415925
#define PI 取消PI的值
这里PI看起来像一个变量,但它与变量没有任何关系,它只是一个符号或标志,在 程序代码编译前,此符号会用一组指定的字符来代替
3.14159265 不是一个数值,只是一个字符串,不会进行检查
在编译前,预处理器会遍历代码,在它认为置换有意义的地方,用字符串PI的定义值(3.14159265)来代替
在注释或字符串中的PI不进行替换
在C中常以#define来定义符号常量,但在C++中最好使用const 来定义常量
#define PI 3.14159265
const long double PI=3.14159265;
两者比较下,前者没有类型的指定容易引起不必须的麻烦,而后者定义清楚,所以在C++中推荐使用const来定义常量
#define的缺点:
1)不支持类型检查
2)不考虑作用域
3)符号名不能限制在一个命名 空间中
2>#undef 删除#define定义的符号
#define PI 3.14159265
... //之间所有的PI都可以被替换为3.14159265
#undef PI
之后不再有PI这个标识符
3>定义宏
#define Print(Var) count<<(Var)<<endl
用宏名中的参数带入语句中的参数
宏后面没有;号
Print(Var)中的Print和(之间不能有空格,否则(就会被解释为置换字符串的一部分
#define Print(Var, digits) count << setw(digits) << (Var) << endl
调用
Print(ival, 15)
预处理器就会把它换成
cout << setw(15) << (ival) << endl;
所有的情况下都可以使用内联函数来代替宏,这样可以增强类型的检查
template<class T> inline void Print (const T& var, const int& digits)
{
count<<setw(digits)<<var<<endl;
}
调用
Print(ival, 15);
使用宏时应注意的易引起的错误:
#define max(x,y) x>y?x:y;+
调用 result = max(myval, 99); 则换成 result = myval>99?myval:99; 这个没有问题是正确的
调用 result = max(myval++, 99); 则换成 result = myval++>99?myval++:99; 这样如果myval>99那么myval就会递增两次,这种情况下()是没什么用的如result=max((x),y)则 result = (myval++)>99?(myval++):99;
再如
#define product(m,n) m*n
调用
result = product(5+1,6);则替换为result = 5+1*6; 所以产生了错误的结果,此时应使用()把参数括起
#define product(m,n) (m)*(n)
则result = product(5+1,6);则替换为result = (5+1)*(6); 所以产生了错误的结果,此时应使用()把参数括起
结论: 一般用内联函数来代替预处理器宏
技巧:
1)给替换变量加引号
#define MYSTR "I love you"
cout << MYSTR ; //I love you而不是"I love you"
如果
cout << "MYSTR" ; //则会输出"MYSTR"而不是"I love you"
可以这样做
cout << #MYSTR ; //则会输出 "I love you"即cout << "\"I love you\"";
2)在宏表达式中连接几个参数
如
#define join(a,b) ab 这样不会理解为参数a的值与参数b的值的连接,即如join(10,999)不会理解为10999而是把ab理解为字符串,即输出ab
这时可以
#define join(a,b) a##b
则join(10,999)就会输出10999
3)逻辑预处理器指令
#if defined CALCAVERAGE 或 #ifdef CALCAVERAGE
int count=sizeof(data)/sizeof(data[0]);
for(int i=0; i<count; i++)
average += data;
average /= count;
#endif
如果已经定义符号CALCAVERAGE则把#if与#endif间的语句放在要编译的源代码内
防止重复引入某些头文件
#ifndef COMPARE_H
#define COMPARE_H 注意: 这里只是定义一个没有值的符号COMPARE_H, 下面的namespace compare不是COMPARE_H的 内容,这里的定义不像是定义一个常量或宏,仅仅定义一个符号,指出此符号已定义,则就会有下面的 内容namespace compare{...
namespace compare{
double max(const double* data, int size);
double min(const double* data, int size);
}
#endif
比较
#define VERSION \
3
因为有换行符\ 所以上句等价于 #define VERSION 3
由此可以看出#define COMPARE_H与namespace compare是独立没有关系的两个行
也可以这样用
#if defined block1 && defined block2
...
#endif
#if CPU==PENTIUM4
...
#endif
#if LANGUAGE == ENGLISH
#define Greeting "Good Morning."
#elif LANGUAGE == GERMAN
#define Greeting "Guten Tag."
#elif LANGUAGE == FRENCH
#define Greeting "Bonjour."
#else
#define Greeting "Hi."
#endif
std::cout<<Greeting << std::endl;
#if VERSION == 3
...
#elif VERSION == 4
...
#else
...
#endif
5)标准的预处理器宏
__LINE__ 当前源文件中的代码行号,十进制整数
__FILE__ 源文件的名称,字符串字面量
__DATE__ 源文件的处理日期,字符串字面量,格式mmm dd yyyy其中mmm是月份如Jan、Feb等 dd是01-31 yyyy是四位的年份
__TIME__ 源文件的编译 时间,也是字符串字面量格式是hh:mm:ss
__STDC__ 这取决于实现方式,如果编译器选项设置为编译标准的C代码,通常就定义它,否则就不定义它
__cplusplus 在编译C++ 程序时,它就定义为199711L
使用#line可以修改__FILE__返回的字符串
如
#line 1000 把当前行号设置为1000
#line 1000 "the program file" 修改__FILE__返回的字符串行号改为了1000,文件名改为了"the program file"
#line __LINE__ "the program file" 修改__FILE__返回的字符串行号没变,文件名改为了"the program file"
cout << "program last complied at "<<__TIME__
<< " on " << __DATE__
<< endl;
6)#error
在预处理阶段,如果出现了错误,则#error指令可以生成一个诊断 消息,并显示为一个编译错误,同时中止编译
#ifndef __cplusplus
#error "Error - Should be C++"
#endif
7)#pragma
专门用于实现预先定义好的选项,其结果在编译器说明文档中进行了详细的解释。编译器未识别出来的#pragma指令都会被忽略
8)assert()宏
在标准库头文件<cassert>中声明
用于在 程序中 测试一个逻辑表达式,如果逻辑表达式为false, 则assert()会终止 程序,并显示诊断 消息
用于在条件不满足就会出现重大错误,所以应确保后面的语句不应再继续执行,所以它的应用非常灵活
注意: assert不是错误处理 机制,逻辑表达式的结果不应产生负面效果,也不应超出 程序员的控制(如找开一个文件是否成功), 程序应提供适当的代码来处理这种情况
assert(expression);
assert(expression) && assert(expression2);
可以使用#define NDEBUG来关闭断言 机制
#include <iostream>
#include <cassert>
using std::cout;
using std::endl;
int main()
{
int x=0;
int y=0;
cout<<endl;
for(x=0; x<20; x++)
{
cout<<"x= "<<x <<" y= "<<y<<endl;
assert(x<y); //当x>=y与x==5时,就报错,并终止 程序的执行
}
return 0;
}
分享到:
相关推荐
在C++编程中,预编译命令是开发者...总结来说,C++中的预编译命令和预编译头文件是优化编译过程、增强代码可移植性和管理编译行为的重要工具。理解和正确使用这些技术可以提升开发效率,同时保证代码的质量和可维护性。
C++预编译命令在编程过程中起到至关重要的作用,它们帮助开发者定制编译器的行为,以适应特定的系统环境和项目需求。`#pragma`指令是C++预处理中的一个特性,它允许程序员向编译器发出特定的指令,这些指令通常与...
C++预编译命令在编程过程中起到至关重要的作用,它们主要用来优化编译过程,提供特定平台或编译器的特性支持,以及帮助开发者更好地管理和控制代码。在本文中,我们将详细探讨一些常见的#pragma指令,这些指令对于...
- 预处理器指令是C++源代码中以`#`字符开头的特殊命令。它们在编译过程之前由预处理器处理。 - 常见的预处理器指令包括`#include`用于引入头文件,`#define`用于定义宏等。 - `#include <iostream>`:此指令告诉预...
GCC,全称GNU Compiler Collection,是一个广泛使用的开源编译器套件,最初是为C语言设计的,但...了解和熟练掌握这些命令和选项,对于任何C/C++开发者来说都是至关重要的,能够提高开发效率,确保代码质量和可维护性。
预处理指令部分介绍了宏定义、条件编译等预处理器功能,使读者能够编写更灵活、可维护的代码。此外,库函数和库类的介绍则覆盖了标准输入输出、字符串操作、数学计算、文件处理等常用功能,提供了丰富的编程工具箱。...
- 预处理:处理`#define`等预编译指令和文件包含。 - 编译:将C源代码转化为汇编代码。 - 汇编:将汇编代码转化为机器码。 - 链接:合并多个目标文件及库文件,生成最终的可执行文件。 4. **查看文件和目录**:...
C++编译过程中的预处理阶段会处理源代码中的预编译指令,这些指令以#号开头,如#include指令用于引入头文件,#define指令用于定义宏。预处理器会在编译之前处理这些指令,以便生成编译器可以直接处理的源代码文本。 ...
为了安装`pyltp`,你需要确保系统已经安装了Python 3.5或3.6版本,因为预编译的64位版本是为这两个版本设计的。你可以通过命令行运行`python --version`来检查当前的Python版本。如果版本不符,你需要先下载并安装...
在VS2012编译环境中,通过双击“vc”快捷方式启动编译过程,这通常会调用Visual Studio的命令提示符,用户可以在其中运行MSBuild或相关编译命令。编译流程主要包括以下几个步骤: - **预处理**:预处理器cpp.exe会...
- 如果选择其他来源,请确保下载的是可信版本,例如从 [http://www.rupeng.com/index.shtml](http://www.rupeng.com/index.shtml) 提供的预编译包 `mingw.zip`。 2. **安装EditPlus**: EditPlus是一款非常强大的...
在CMake中,管理项目依赖库是构建过程中的...总结,CMake提供了一套灵活的机制来管理项目依赖,无论是从源码编译还是使用预编译库。理解这些机制对于构建复杂的C++项目至关重要,有助于保持构建过程的整洁和可维护性。
总结来说,预编译的OpenCV 4.5.0 for x86 32位系统是一个宝贵的资源,它消除了手动编译的复杂性,让开发者能够专注于他们的核心任务——利用OpenCV的强大功能来解决实际的计算机视觉问题。无论你是新手还是经验丰富...
总结来说,这个资源为CentOS 7.4.1708提供了方便的GCC C++编译环境,通过预打包的RPM包解决了可能存在的缺失或版本不匹配的问题,特别适合需要在Docker容器中进行C++开发的场景。用户只需简单几步操作,即可拥有一个...
3. **使用预编译的二进制包**:许多Python包提供了预编译的二进制版本,可以直接通过pip安装,无需本地编译环境。例如,你可以尝试用`pip install package_name --no-binary=:all:`来安装。 4. **安装Visual Studio...
IBM XL C/C++ for AIX 编译器支持多种编译命令,以适应不同的编译需求和源代码标准。主要的编译命令包括: - `xlc`:用于编译C源文件,支持ANSI C89、ISO C99和IBM语言扩展。 - `xlc++`:用于编译C++源文件,支持...
C 和 C++ 中的宏是一种预处理器特性,用于在编译前替换文本。宏定义允许程序员用一个标识符(宏名)来代表一个值或一个复杂的表达式,从而简化代码并提高可读性。以下是对宏使用的一些关键点的详细说明: 1. **无参...
2. **预处理**:编译器首先执行预处理器的工作,它处理`#include`指令,展开宏定义,以及处理条件编译指令(如`#ifdef`, `#ifndef`, `#endif`等)。 3. **编译**:经过预处理后的代码被送入编译器进行编译。编译器...