#pragma的常用方法讲解

article/2025/10/7 1:55:55

概述

我们在写代码时,总会遇到头文件多次包含的情况,刚开始时我们使用宏定义进行控制,之后发现有#pragma once这样简单的东西,当时是很兴奋,以为#pragma就这一种用法。唉~,现在想想当时还是年轻啊,不过还是年轻好啊。

1、什么是预处理

预处理是将源文件的文本作为翻译的第一阶段操作的文本处理步骤。 预处理不会分析源文本,但会为了查找宏调用而将源文本细分为标记。 主要包括了下面三个方面:

  1. 预处理指令
  2. 预处理运算符
  3. 预定义宏,这个有很多了,比如__FILE__、__LINE__和__DATA__等。

其中预处理指令包括:
在这里插入图片描述
预处理运算符包括:
在这里插入图片描述
这里有很多预处理指令符,今天我只整理pragma了。

2、什么是pragma

#pragma指令的作用是:用于指定计算机或操作系统特定的编译器功能。C 和 C++ 的每个实现均支持某些对其主机或操作系统唯一的功能。 例如,某些程序必须对将数据放入的内存区域进行准确的控制或控制某些函数接收参数的方式。 在保留与 C 和 C++ 语言的总体兼容性的同时,#pragma 指令使每个编译器均能够提供特定于计算机和操作系统的功能。

根据定义,#pragma指令是计算机或操作系统特定的,并且通常对于每个编译器而言都有所不同。 #pragma指令可用于条件语句以提供新的预处理器功能,或为编译器提供实现所定义的信息。

语法是:

#pragma  token-string
__pragma( token-string )	//__pragma 关键字是特定于 Microsoft 编译器的

token-string 是一系列字符,这些字符提供了特定的编译器指令和参数(如果有)。 数字符号"#" 必须是位于包含#pragma指令行上的第一个非空白字符;空白字符可以分隔数字符号和词“pragma”。 在 #pragma 之后,编译转换器可分析为预处理标记的所有文本。 #pragma 的参数受宏展开的约束,如果编译器发现它无法识别的杂注,则它会发出警告并继续编译

Microsoft C 和 C++ 编译器识别以下指令:
在这里插入图片描述
注:标注1的仅受C++编译器支持。

好,下面就对一些常用的指令进行说明,有一些不常用,或者我们上层不常用的就不介绍了,因为我也不懂(没有时间应用,看也看不懂)。

2.1 #pragma once

大家应该都知道:指定该文件在编译源代码文件时仅由编译器包含(打开)一次
使用 #pragma once 可减少生成次数,和使用预处理宏定义来避免多次包含文件的内容的效果是一样的,但是需要键入的代码少,可减少错误率,例如:

使用#progma once
#pragma once  
// Code placed here is included only once per translation unit 使用宏定义方式
#ifndef HEADER_H_ 
#define HEADER_H_  
// Code placed here is included only once per translation unit  
#endif // HEADER_H_  

2.2 #pragma message(messageString)

不中断编译的情况下,发送一个字符串文字量到标准输出。message编译指示的典型运用是在编译时显示信息,例如:

#if _M_IX86 >= 500  		//查看定义的宏有没有大于500,或者有时用作检查有没有定义宏
#pragma message("_M_IX86 >= 500")  
#endif 

messagestring 参数可以是扩展到字符串的宏,您可以通过任意组合将此类宏与字符串串联起来,例如:

#pragma message( "Compiling " __FILE__ )   		//显示被编译的文件
#pragma message( "Last modified on " __TIMESTAMP__ ) 	//文件最后一次修改的日期和时间

如果在 message 杂注中使用预定义的宏,则该宏应返回字符串,否则必须将该宏的输出转换为字符串,例如:

#define STRING2(x) #x  
#define STRING(x) STRING2(x)  #pragma message (__FILE__ "[" STRING(__LINE__) "]: test")   //注意把行号转成了字符串

2.3、#pragma waring(…)

启用编译器警告消息的行为和选择性修改,语法为:

#pragma warning( warning-specifier : warning-number-list [,warning-specifier : warning-number-list...] )#pragma warning( push[ , n ] )    
#pragma warning( pop )

warning-specifier能够是下列值之一:

在这里插入图片描述
估计看这个表会头晕,我来举几个例子:

#pragma warning( disable : 4507 34; once : 4385; error : 164 )   //这1行跟下面3行效果一样#pragma warning( disable : 4507 34 ) 	//不发出4507和34警告,即有4507和34警告时不显示
#pragma warning( once : 4385 )  		//4385警告信息只报告一次
#pragma warning( error : 164 )  		//把164警告信息作为一个错误

注意:对于范围 4700-4999 内的警告编号(与代码生成相关联),在编译器遇到函数的左大括号时生效的警告状态将对函数的其余部分生效。 在函数中使用 warning 杂注更改编号大于 4699 的警告的状态只会在函数末尾之后生效。 以下示例演示如何正确放置 warning 杂注来禁用代码生成警告消息然后还原该消息

#pragma warning(disable:4700)  //不发生4700警告
void Test() 		//这里之前的状态管用,若把上面一行移动到函数里面,则照常发生警告
{  int x;  int y = x;   	//没有C4700的警告#pragma warning(default:4700)   //4700的警告恢复到默认值
}  int main() 
{  int x;  int y = x;   //发生C4700的警告
}  

下面来看warning推送和弹出的用法,语法为:

#pragma warning( push [ ,``n ] )    
#pragma warning( pop )

其中 n 表示警告等级(1 到 4)。

warning( push )指令存储每个警告的当前警告状态。
warning( push, n)指令存储每个警告的当前状态并将全局警告级别设置为 n。
warning( pop )指令 弹出推送到堆栈上的最后一个警告状态。
在 push 和 pop 之间对警告状态所做的任何更改都将被撤消

#pragma warning( push )  
#pragma warning( disable : 4705 )  
#pragma warning( disable : 4706 )  
#pragma warning( disable : 4707 )  
// Some code  						//代码的书写,这里不会发出4705、4706、4707的警告
#pragma warning( pop )   			//会将每个警告(包括4705、4706、4707)的状态还原为代码开始的状态

当你编写头文件时,你能用push和pop来保证任何用户修改的警告状态不会影响正常编译你的头文件。在头文件开始的地方使用push,在结束地方使用pop。例如,假定你有一个不能顺利在4级警告下编译的头文件,下面的代码改变警告等级到3,然后在头文件的结束时恢复到原来的警告等级。

#pragma warning( push, 3 )    
// Declarations/ definitions    		//要书写的代码
#pragma warning( pop )

2.4、#pragma comment(comment-type [,“commentstring”])

该指令将一个注释记录放入一个对象文件或可执行文件中。

comment-type 是一个预定义的标识符(如下所述,一共5个),它指定了注释记录的类型。 可选 commentstring 是一个字符串,它提供了某些注释类型的附加信息。 由于 commentstring 是一个字符串,因此它遵循有关转义字符、嵌入的引号 (") 和串联的字符串的所有规则。

compiler
将编译器的名称和版本号置于对象文件中。 此注释记录将被链接器忽略。 如果为此记录类型提供 commentstring 参数,则编译器会生成警告。

exestr
将 commentstring 置于对象文件中。 在链接时,会将该字符串置于可执行文件内。 加载可执行文件时,不会将字符串加载到内存中;但是,可以使用在文件中查找可打印字符串的程序来找到它。 此注释记录类型的一个用途是将版本号或类似信息嵌入可执行文件中。

lib(这个最常用了)
将库搜索记录置于对象文件中。 此注释类型必须带有包含您希望链接器搜索的库的名称(和可能的路径)的 commentstring 参数。 库名称遵循对象文件中的默认库搜索记录;链接器会搜索此库,这就像在命令行上对其命名一样,前提是未使用 /nodefaultlib 指定库。 可以将多个库搜索记录置于同一个源文件中;各个记录将以其在源文件中显示的顺序出现在对象文件中。

如果默认库和添加的库的顺序很重要,则使用 /Zl 开关进行编译会阻止将默认库名称置于对象模块中。 然后,可使用另一个注释指令在添加的库的后面插入默认库的名称。 与这些指令一起列出的库将以其在源代码中的发现顺序出现在对象模块中。

#pragma  comment(lib, "comctl32.lib")
#pragma comment( lib, "mysql.lib" )

linker
将链接器选项置于对象文件中。 可以使用注释类型来指定链接器选项,而不是将其传递到命令行或在开发环境中指定它。 例如,可以指定 /include 选项来强制包含符号:

#pragma comment(linker, "/include:__mySymbol") 

user
将一般注释置于对象文件中。 commentstring 参数包含注释文本。 此注释记录将被链接器忽略。

2.5、#pragma region … /endregion …

#pragma region是Visual C++中特有的预处理指令。它可以让你折叠特定的代码块,从而使界面更加清洁,便于编辑其他代码。折叠后的代码块不会影响编译。你也可以随时展开代码块以进行编辑等操作

int main()
{
...
#pragma region demo_region		//这里前面会有折叠符,折叠时提示 demo_region所写内容
int a = 10;
int b = 20;
int c = ADD( a + b ); 
#pragma endregion demo_region		//需要跟#pragma region配套使用
....
}

折叠代码块的方法:如同Visual C++中折叠函数、类、命名空间,当代码被包含在如上所述的指令之间后,#pragma region这一行的左边会出现一个“-”号,单击以折叠内容,同时“-”号会变成“+”号,再次单击可以展开代码块

2.6、#pragma alias(…)

指定 short_filename 将用作 long_filename 的别名,语法为:

#pragma include_alias( "long_filename ", "short_filename" )  
#pragma include_alias( <long_filename>, <short_filename> ) 

某些文件系统允许长度超过 8.3 FAT 文件系统限制的头文件名。 因为较长的头文件名的前八个字符可能不是唯一的,因此编译器无法简单地将较长的名称截断为 8.3。 每当编译器遇到 long_filename 字符串时,都将替换 short_filename,并改为查找头文件 short_filename。 此杂注必须在相应的 #include 指令之前出现。

要搜索的别名必须完全符合规范无论是大小写、拼写还是双引号或尖括号的使用。 include_alias 杂注对文件名执行简单的字符串匹配;将不执行任何其他文件名验证。 例如:

#pragma include_alias("mymath.h", "math.h")  	//要在头文件引用之前定义#include "./mymath.h"  			//多了"./",必须完全匹配
#include "sys/mymath.h"  		//多了"sys/ "

您可以使用 include_alias 杂注将任何头文件名映射到另一个头文件名。 例如

#pragma include_alias( "api.h", "c:\version1.0\api.h" )  
#pragma include_alias( <stdio.h>, <newstdio.h> )  
#include "api.h"  
#include <stdio.h>  

不要将用双引号括起来的文件名与用尖括号括起的文件名组合在一起。 例如,给定上述两个 #pragma include_alias 指令,编译器将不对下列 #include 指令执行任何替换:

#include <api.h>  
#include "stdio.h"  

2.7#pragma pack([ show ] | [ push | pop ] [, identifier ] , n)

指定结构、联合和类成员的封装对齐。其实就是改变编译器的内存对齐方式。这个功能对于集合数据体使用,默认的数据的对齐方式占用内存比较大,可进行修改。

在没有参数的情况下调用pack会将n设置为编译器选项/zp中设置的值。如果未设置编译器选项,windows默认为8,linux默认为4

具体的使用方法为,其中n的取值必须是2的幂次方,即1、2、4、8、16等:

1. #pragma pack(show)     以警告信息的形式显示当前字节对齐的值.
2. #pragma pack(n)        将当前字节对齐值设为 n .
3. #pragma pack()         将当前字节对齐值设为默认值(通常是8) .
4. #pragma pack(push)     将当前字节对齐值压入编译栈栈顶.
5. #pragma pack(pop)      将编译栈栈顶的字节对齐值弹出并设为当前值.
6. #pragma pack(push, n)  先将当前字节对齐值压入编译栈栈顶, 然后再将 n 设为当前值.
7. #pragma pack(pop, n)   将编译栈栈顶的字节对齐值弹出, 然后丢弃, 再将 n 设为当前值.8. #pragma pack(push, identifier)        将当前字节对齐值压入编译栈栈顶, 然后将栈中保存该值的位置标识为 identifier .
10. #pragma pack(pop, identifier)        将编译栈栈中标识为 identifier 位置的值弹出, 并将其设为当前值. 注意, 如果栈中所标识的位置之上还有值, 那会先被弹出并丢弃.
11. #pragma pack(push, identifier, n)    将当前字节对齐值压入编译栈栈顶, 然后将栈中保存该值的位置标识为 identifier, 再将 n 设为当前值.
12. #pragma pack(pop, identifier, n)     将编译栈栈中标识为 identifier 位置的值弹出, 然后丢弃, 再将 n 设为当前值. 注意, 如果栈中所标识的位置之上还有值, 那会先被弹出并丢弃.注意: 如果在栈中没有找到 pop 中的标识符, 则编译器忽略该指令, 而且不会弹出任何值.

使用时最好是成对出现的,要不容易引起错误,设置之后记得用完给恢复,如:

#pragma pack(n) 	//设置以n个字节为对齐长度
struct 
{int  ia;char cb;
}
#pragma pack ()  	//弹出n个字节对齐长度,设置默认值为对齐长度

如果想给单独一个结构体设置对齐长度还可以使用C++ 11 标准中的alignas。

但是不是设置#pragma pack(n)就按照设置的字节进行对齐呢?其实并不是这样的,实际的对齐字节数要符合以下规则:
1、若设置了对齐长度n,实际对齐长度=Min(设置字节长度,结构体成员的最宽字节数)。若没有设置n,实际对齐长度 = Min(结构体成员的最宽字节数,默认对齐长度)。
2、每个成员相对于首地址的偏移量(offset)都是实际对齐长度的整数倍,若不满足编译器进行填充。
3、数据集合的总大小为实际对齐长度的整数倍,若不是编译器进行填充。

假设默认对齐为8时,看几个例子:

#pragma pack(n)
struct Stu1
{short  	sa;	//2个字节char 	cb;	//1个字节int		ic;	//4个字节char 	cd;	//1个字节
}
#pragma pack()cout << sizeof(Stu1) << endl;

若n为1:实际对齐长度为1 = Min(1,8)。这个就不用解释了,相当于各个元素相加,总长度为8。
若n为2:实际对齐长度为2 = Min(2,8)。sa占两个字节,不需要补齐。cb首地址偏移为2个字节,满足规则二。ic的首地址偏移3(2+1)个字节,不能满足规则二,填充一个字节到4。cd的首地址偏移为8个字节,满足规则。现在相加的8+1=9个字节,不满足规则三,填充一个字节,总长度为10;
若n为4:实际对齐长度为4 = Min(4,8)。这个就不用介绍了,默认时就用的这个。总长度为12。
若n>4:实际对齐长度为 4 = Min(设置字节长度,结构体成员的最宽字节数)。总长度为12。

2.8 其他

上面的有用过的,也有整理的实现现学的,我感觉有用处的。当然还有一些别的,但是我也没有看懂这里就不整理了,有需要的可以查看我下面的参考资料第2条,我列几个别的大概是干什么的。

#pragma hdrstop 停止编译头文件的,给我的感觉有点像“使用预编译头文件”
#pragma inline_depth( [0…255]) 设置内联调用深度的

感谢大家,我是假装很努力的YoungYangD(小羊)。

参考资料:
https://blog.csdn.net/jx_kingwei/article/details/367312
https://msdn.microsoft.com/zh-cn/library/d9x1s805.aspx


http://chatgpt.dhexx.cn/article/7UystCq2.shtml

相关文章

Attribute(特性)

一向都觉得.NET的Attribute好神秘。一个方框框住的东西&#xff0c;置身于类、方法的头部&#xff0c;本身不在类或方法里面&#xff0c;但又会起作用&#xff0c;有时作用还很大&#xff0c;仿佛充满了魔力。简直给人一种无冕之王&#xff0c;幕后之黑手的感觉&#xff01; 某…

attribute的用法--C#

一直以来都没理解attribute是个什么东西&#xff0c;也没怎么用&#xff0c;但是看msdn或者git上源码使用的还是蛮频繁的&#xff0c;今天好好整理了下&#xff0c;写下自己的理解和例子&#xff1a; attribute主要用来说明代码段的的信息&#xff0c;标志等&#xff1b;可以一…

attribute属性

attribute属性 __attribute__介绍 attribute的格式. __attribute__((attribute-list))attribute属性可以在编译的时候告诉编译器函数, 结构体的属性是什么, 进行某些编译优化, 也可以提供更加准确的错误检查. attribute是GNU特有的特性 这里主要说明attribute的4个属性, 分…

【C#】Attribute

原文链接&#xff1a;http://bbs.51aspx.com/showtopic-33963.html 前言 作为一个.NET开发人员&#xff0c;了解Attribute的重要性&#xff0c;用.NET大师Jeffrey Richter的话就是“任何.NET Framework 开发人员都有必要对定制attribute有一个牢靠的掌握”&#xff0c;所以掌…

attributes() 函数

查看更多 https://www.yuque.com/docs/share/a6cc2c96-9824-4903-acb8-284f4ebeb4fb

__attribute__ 用法

转自&#xff1a;http://www.cnblogs.com/astwish/p/3460618.html GNU C 的一大特色就是__attribute__ 机制。__attribute__ 可以设置函数属性&#xff08;Function Attribute &#xff09;、变量属性&#xff08;Variable Attribute &#xff09;和类型属性&#xff08;Type …

View绘制体系(三)——AttributeSet与TypedArray详解

View绘制体系&#xff08;三&#xff09;——AttributeSet与TypedArray详解 前言 上篇博客中讲了LayoutInflater.inflate机制&#xff0c;其中提到了AttributeSet和XmlPullParser两个接口&#xff0c;这里我们来详细的了解一下Android中提供的AttributeSet接口和它与XmlPullPa…

Android自定义View(三)自定义属性AttributeSet

自定义View的时候通常需要提供一些自定义属性&#xff0c;自定义属性非常简单&#xff0c;只需要在res资源目录的values目录下创建一个attrs.xml的属性定义文件&#xff0c;然后在该文件中定义相应的属性&#xff0c;并在自定义View的构造函数中获取并设置自定义属性的默认值即…

web搜索框的制作(必应)

前两天没事突然对搜索来了兴趣&#xff0c;我一直在想搜索框中我们输入一些字或者字母&#xff0c;为何下面就会有一些自动补齐的相关搜索&#xff0c;比如我在搜索输入框中输入一个字母e&#xff0c;下面就会出现饿了么&#xff0c;e租宝&#xff0c;ems等相关的搜索链接。然后…

html怎么调搜索框宽高,百度站内搜索css:输入框宽度及样式自定义

近日网站使用了百度站内搜索api&#xff0c;目的是为了提高站内搜索的速度&#xff0c;减轻查询站内数据库带来的服务器压力。 不过在使用百度站内搜索api(生效范围&#xff1a;*webkaka.com/*)后发现一个问题&#xff0c;不同的频道模版造成排版不合适的后果&#xff0c;如搜索…

织梦手机站站内搜索

今天在做手机网站发现一个问题&#xff0c;当在手机是使用搜索功能时马上就跳转到电脑端网站去了&#xff0c;在手机上无法使用。在网上找半天没有找到解决的办法&#xff0c;后来自己想通了&#xff0c;下面告诉大家怎么样简单的实现这个功能&#xff01;我的手机站是在m/这个…

基于 Elasticsearch 的站内搜索引擎实战

站内搜索&#xff0c;可以认为是针对一个网站特性内容的搜索功能。由于内容、格式可控&#xff0c;站内搜索比全网搜索的实现要简单很多。 简书这个网站本身自带一个搜索&#xff0c;但是缺乏针对个人文章的搜索&#xff0c;所以本文的实战内容是解决这个痛点。 代码在 https…

使用swiftype实现站内搜索

本人博客opiece.me&#xff0c;欢迎访问。 前言 首先&#xff0c;以下的内容是基于最新的swifytpe的教程&#xff0c;应该是2.0.0。 站内搜索顾名思义就是将范围限定在你的网站内&#xff0c;以此范围进行关键字搜索。 常见的站内搜索是google和baidu的&#xff0c;但是现在…

Compass实战 站内搜索

今天早上打算对这两天学习的Lucene以及Compass总结一下,想来想去,还是写个小项目来验证最好了。于是就有了今天的这篇文章。难易程度适合对于Compass或者Lucene刚入门的童鞋,大牛看到后望轻喷 :-) 项目预览项目需求项目目录核心处理 发帖部分查询部分总结项目预览 项目需求 …

html中的搜索

目录 hello&#x1f604; form表单&#x1f349; form的语法&#x1f34a; from的属性&#x1f34a; 提交&#xff1f;重置&#xff1f;&#x1f34a; 表单按钮&#xff08;html&#xff09;&#x1f50d; JavaScript提交表单&#x1f50d; JavaScript重置表单&#x1f…

必应(Bing)的站内搜索 site:<域名> <搜索内容>

最近在备考OCP&#xff0c;发现有一个网站的题库很好&#xff0c;就是www.examtopics.com&#xff0c;有很多Oracle的考题&#xff0c;都是在这里面搜到的&#xff0c;而且每道题都有人讨论。 为了加快搜索速度&#xff0c;提高精度&#xff0c;可以用Bing在这个网站内搜索&am…

百度站内搜索使用教程

最近做了一个博客CMS网站&#xff0c;用到了百度站内搜索&#xff0c;做一些必要的笔记&#xff0c;一来是对自己学习的知识的巩固&#xff0c;二来对有同样问题的人有参考作用 文章目录 一 使自己的网站被百度收录二 获取百度站内搜索代码三 总结 声明一下&#xff0c;我本人很…

利用免费的必应 Bing 自定义搜索打造站内全文搜索

简介 百度的站内搜索不做了&#xff0c;唉&#xff0c;果然免费的不永久。我们看看 Bing 的&#xff0c;每个月有 1000 次免费的调用 bing search api 的次数。不同客户可以多申请几个就行了。 申请入口&#xff1a; https://www.customsearch.ai&#xff0c;官方简介页面官方…

html百度站内搜索代码,网站添加百度站内搜索的教程

zblog博客程序中可以在侧边栏中添加搜索功能&#xff0c;但是让人郁闷的是如果没针对搜索使用搜索插件&#xff0c;那情况简直让人抓狂&#xff0c;还好我们可以使用百度的站内搜索功能&#xff0c;一方面可以节省网站的资源&#xff0c;另一方面可以增加百度的收率几率。 关于…

站内搜索

使用“site:”或者“domain:”来实现站内搜索 如果你想在一个特定的网站上来进行搜索&#xff0c;在众多庞大的信息流中找到你想要的信息&#xff0c; 在上篇中(http://blog.csdn.net/liunian02050328/article/details/8220379)介绍在java编程的环境下实现站内搜索&#xff0c;…