工作中遇到的:

一个头文件中的:

#pragma warning(disable:4996)
#pragma warning(disable:4244)
#pragma warning(disable:4267)

不理解意思,遂查☟

C/C++中的预编译指令

程序的编译过程可以分为预处理、编译、汇编三部分,其中预处理是首先执行的过程,预处理过程扫描程序源代码,对其进行初步的转换,产生新的源代码提供给编译器。
预处理过程读入源代码之后,会检查代码里包含的预处理指令,完成诸如包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码的工作。下面介绍一些C/C++中预编译的指令。

一 #指令

预处理指令以#号开头,并且#号必须是该行除了任何空白字符外的第一个字符。

#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。

单纯一个#号表示空指令,没有任何作用。

二 #include指令

#include预处理指令的作用是在指令处展开被包含的文件。展开被包含的文件之后,在代码就可以正常地调用该文件中所声明的变量和函数。#include指令有两种使用方法:

#include <xxx.h>
#include "xxx.h"

第一种方法将待包含的头文件使用尖括号括起来,预处理程序会在系统默认目录或者括号内的路径查找,通常用于包含系统中自带的公共头文件。

第二种方法将待包含的头文件使用双引号引起来,预处理程序会在程序源文件所在目录查找,如果未找到则去系统默认目录查找,通常用于包含程序作者编写的私有头文件。

文件包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。在大型的程序中可能会产生重复包含的问题,如

#include "a.h"
#include "b.h"

一个程序包含了a.h和b.h两个头文件,但a.h和b.h可能同时又都包含了c.h,于是该程序就包含了两次c.h,这在一些场合下会导致程序的错误,可以通过下面的条件编译进行解决。

三 #define、#undef指令

#define指令定义了一个标识符及一个串,标识符称为宏名,源程序中宏名的每次出现都会用其定义的串进行替换,称为宏替换。

#undef指令取消一个已定义的宏。

宏一般使用大写字母定义,其可以出现在程序的任意地方。宏替换仅仅是以文本串代替宏标识符的过程,该过程很容易出现一些逻辑上的错误,需要仔细处理一些关于括号的问题。

以下代码用宏定义了一个常量PI,但C++中建议使用const进行常量定义,因为宏替换并不会进行类型匹配之类的安全性检查。同时用宏定义了一个MAX函数,其好处是没有函数调用的额外开销,运行速度较快,但容易出错,而且大量的宏替换会增加代码的长度。

void test1()
{
#define PI 3.14
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
cout << PI << endl; //3.14
cout << MAX(2, 3) << endl; //3 #undef PI
//cout << PI << endl; //编译出错
}

另外还有两个特殊的运算符:

  1. 宏定义中的#运算符把跟在其后的参数转换成一个字符串称为字符串化运算符。
  2. 宏定义中的##运算符把出现在##两侧的参数合并成一个符号。
void test2()
{
#define CAT(n) "ABC"#n
cout << CAT(123) << endl; //ABC123 #define NUM(a,b) a##b
#define STR(a,b) a##b
cout << NUM(1, 2) << endl; //12
cout << STR("Hello", "World") << endl; //HelloWorld
}

四 #if、#elif、#else、#endif指令

这几个指令称为条件编译指令,可对程序源代码的各部分有选择地进行编译。

跟一般的if、else if、else语句类似,如果一个条件上的值为真,则编译它对应的代码,否则提过这些代码,测试下一个条件上的值是否为真。注意,作为条件的表达式是在编译时求值的,它必须仅含常量及已定义过的标识符,不可使用变量,也不可以含有操作符sizeof(sizeof也是编译时求值)。

命令#endif标识一个#if块的结束。

void test3()
{
#define OPTION 2 #if OPTION == 1
cout << "Option: 1" << endl;
#elif OPTION == 2
cout << "Option: 2" << endl; //选择这句
#else
cout << "Option: Illegal" << endl;
#endif
}

五 #ifdef、#ifndef、#endif指令

这几个也是条件编译指令,其检查后面指定的宏是否已经定义,然后根据检查结果选择是否要编译后面语句。其中#ifdef表示”如果有定义“,#ifndef表示”如果没有定义“。

这个通常可以用于防止重复包含头文件的问题,如下所示:

#ifndef MYHEAD_H

#define MYHEAD_H
#include "myHead.h" #endif

六 #line指令

C语言中可以使用__FILE__表示本行语句所在源文件的文件名,使用__LINE__表示本行语句在源文件中的位置信息。#line指令可以重新设定这两个变量的值,其语法格式为

#line number["filename"]

其中第二个参数文件名是可省略的,并且其指定的行号在实际的下一行语句才会发生作用。

void test4()
{
cout << "Current File: " << __FILE__ << endl; //Current File: d:\test.cpp
cout << "Current Line: " << __LINE__ << endl; //Current Line: 48
#line 1000 "wrongfile"
cout << "Current File: " << __FILE__ << endl; //Current File: d:\wrongfile
cout << "Current Line: " << __LINE__ << endl; //Current Line: 1001
}

七 #error指令

#error指令在编译时输出编译错误信息,可以方便程序员检查出现的错误。

void test5()
{
#define OPTION 3
#if OPTION == 1
cout << "Option: 1" << endl;
#elif OPTION == 2
cout << "Option: 2" << endl;
#else
#error ILLEGAL OPTION! //fatal error C1189: #error : ILLEGAL OPTION!
#endif
}

八 #pragma指令

该指令用来来设定编译器的状态或者是指示编译器完成一些特定的动作,它有许多不同的参数。

1. #pragma once

在头文件的最开始加入这条指令可以保证头文件只被编译一次。它可以实现上述使用#ifndef实现不重复包含头文件同样的功能,但可能会有部分编译系统不支持。

2. #pragma message

该指令能够让编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。其使用方法为:#pragma message(“消息文本”)

通过这条指令我们可以方便地记录在是否在源代码中定义过某个宏,如

#define ISPC
#ifdef ISPC
#pragma message("Macro ISPC is defined") //编译输出:Macro ISPC is defined
#endif

3. #pragma warning

该指令能够控制编译器发出警告的方式,其用法举例如:#pragma warning(disable : 4507 34; once : 4385; error : 164)

这个指令有三部分组成,其中disable部分表示忽略编号为4507和34的警告信息,once部分表示编号为4385的警告信息只显示一次,error部分表示把编号为164的警告信息当做错误。

另外,其还有两个用法

  • #pragma warning(push [, n]):保存所有警告信息的现有的警告状态,后面n是可选的,表示把全局警告等级设为n。
  • #pragma warning(pop):弹出最后一个警告信息,取消在入栈和出栈之间所作的一切改动。

具体例如如下:

void test6()
{
#pragma warning(push) //保存编译器警告状态
#pragma warning(disable:4305) //取消4305的警告
bool a = 5; //无警告信息
#pragma warning(pop) //恢复之前的警告转改
bool b = 5; //warning C4305: 'initializing' : truncation from 'int' to 'bool'
}

4. #pragma comment

该指令将一个注释记录放入一个对象文件或可执行文件中。其使用方法为:#pragma comment(comment-type ,["commentstring"])

其中comment-type是一个预定义的标识符,指定注释的类型,应该是compiler,exestr,lib,linker之一。常用的是lib关键字,可以帮我们连入一个库文件。 如

#pragma comment(lib, "my.lib")

5. #pragma hdrstop

该指令表示预编译头文件到此为止,后面的头文件不进行预编译。

6. #pragma resource

该指令表示把指定文件中的资源加入工程,如

#pragma resource "*.dfm"

7. #pragma code_seg

该指令能够设置程序中函数代码存放的代码段,开发驱动程序的时候会使用到。使用方法为:#pragma code_seg(["section-name" [,"section-class"] ])。

8. #pragma data_seg

该指令建立一个新的数据段并定义共享数据。一般用于DLL中,在DLL中定义一个共享的有名字的数据段,这个数据段中的全局变量可以被多个进程共享,否则多个进程之间无法共享DLL中的全局变量。其使用方法为:

#pragma data_seg("MyData")
int value; //共享数据
#pragma data_seg()

9. #pragma pack

该指令规定数据在内存中的对齐长度,具体可以参考这里

#pragma pack(1)
struct S{char a; int b; };
void test7(){ cout << sizeof(S) << endl; } //5

除非注明,文章来自NoAlGo博客原创,转载请保留链接:C/C++中的预编译指令

C/C++中的预编译指令的更多相关文章

  1. iOS中的预编译指令的初步探究

    目录 文件包含 #include #include_next #import 宏定义 #define #undef 条件编译 #if #else #endif #if define #ifdef #i ...

  2. C#中的预编译指令介绍

    原文:C#中的预编译指令介绍 1.#define和#undef 用法: #define DEBUG #undef DEBUG #define告诉编译器,我定义了一个DEBUG的一个符号,他类似一个变量 ...

  3. c/c++中的预编译指令总结

    预处理指令提供按条件跳过源文件中的节.报告错误和警告条件,以及描绘源代码的不同区域的能力.使用术语“预处理指令”只是为了与 C 和 C++ 编程语言保持一致.在 C# 中没有单独的预处理步骤:预处理指 ...

  4. Unity 中使用预编译指令区分平台

    在实际项目开发过程中,我们经常会根据平台来写一些逻辑 #if UNITY_EDITOR #elif UNITY_IPHONE #elif UNITY_ANDROID #endif 使用预编译指令能很好 ...

  5. C#编写中使用预编译指令代替不停的注释

    是不是经常调试某个模块的时候,要打开一堆Console或者Debug.Log,printf 不调试的时候,又关掉.如此繁复的倒腾实在是烦 可以使用预编译指令代替这种做法 #define 自定义字段 . ...

  6. C/C++中的预编译指令(转)

    reference:https://blog.csdn.net/sunshinewave/article/details/51020421 程序的编译过程可以分为预处理.编译.汇编三部分,其中预处理是 ...

  7. C#中的预编译指令介绍[转]

    原文链接 1.#define和#undef 用法: #define DEBUG #undef DEBUG #define告诉编译器,我定义了一个DEBUG的一个符号,他类似一个变量,但是它没有具体的值 ...

  8. c语言中条件编译相关的预编译指令

    一. 内容概述 本文主要介绍c语言中条件编译相关的预编译指令,包括#define.#undef.#ifdef.#ifndef.#if.#elif.#else.#endif.defined. 二.条件编 ...

  9. C#预编译指令

    近日工作涉及到于外部系统交互,对方提供接口:但是在双方系统未联调时,引用外部DLL,相关类实例化,提示异常错误(错误消息正常):后面操作无法进行,那如何写调试代码,即在调试时不运行某段代码,而在正式发 ...

随机推荐

  1. 学习Shell脚本编程(第3期)_在Shell程序中使用的参数

    位置参数 内部参数 如同ls命令可以接受目录等作为它的参数一样,在Shell编程时同样可以使用参数.Shell程序中的参数分为位置参数和内部参数等. 3.1 位置参数 由系统提供的参数称为位置参数.位 ...

  2. 《图解tcp/ip》读书笔记(一)

           我先讲三句话:        一."万物互联的时代到了."我们生活在这样一个互联网急速发展的时代,也许很快就会发现,你能接触到的一切都可以连接到互联网了,电脑.手机这 ...

  3. IIS7 IIS7.5 配置备份

    IIS 7 提供了一个新的命令行工具 Appcmd.exe,可以使用该工具来配置和查询 Web 服务器上的对象,并以文本或 XML 格式返回输出. IIS 备份还原命令如下: 开始-运行-CMD 进入 ...

  4. Bootstrap系列 -- 21. 表单提示信息

    平常在制作表单验证时,要提供不同的提示信息.在Bootstrap框架中也提供了这样的效果.使用了一个"help-block"样式,将提示信息以块状显示,并且显示在控件底部. < ...

  5. Orchard 刨析:Logging

    最近事情比较多,有预研的,有目前正在研发的,都是很需要时间的工作,所以导致这周只写了两篇Orchard系列的文章,这边不能保证后期会很频繁的更新该系列,但我会写完这整个系列,包括后面会把正在研发的东西 ...

  6. C# 无法识别的转义序列

    解决这个问题头两种方法:1.加个"\"进行转义:2.在前面加个@ 示例:我要进入D盘下video文件夹中的ysxs文件夹,写法分别为: D:\\video\\ysxs @" ...

  7. [USACO2002][poj1947]Rebuilding Roads(树形dp)

    Rebuilding RoadsTime Limit: 1000MS Memory Limit: 30000KTotal Submissions: 8589 Accepted: 3854Descrip ...

  8. js中的DOM操作(2)

    1.表格的更加与删除 <!DOCTYPE html> <html> <head> <title>表格操作</title> <style ...

  9. 第五章:Javascript语句

    在javascript中,表达式是短语,那么语句(statement)就是整句或命令.正如英文语句以句号结尾,javascript以分号结尾. 表达式计算出一个值,但语句使某件事发生. “使某件事发生 ...

  10. javascript 漏洞

    1.javascript语言中,每一个对象都有一个对应的原型对象,称为prototype对象.  继承是基于原型的! 2.prototype对象的作用,就是定义所有实例对象共享的属性和方法! 3.“原 ...