预处理 #pragma 命令详解

目录

1.引言        

2.#pragma once

3.#pragma waring(...)

4.#pragma comment

4.1.lib

4.2.linker

5.#pragma region … /endregion …

6.#pragma optimize

7.#pragma message (message string)

8.#pragma omp parallel for

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

10.#pragma alias(…)

11.#pragma code_seg

12.总结


1.引言        

        Pragma是一个预处理器指令,以#pragma开头,#pragma指令可用于条件语句以提供预处理器功能,或为编译器提供实现所定义的信息。用于告诉编译器执行特定的操作或者忽略特定的警告。

        #pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。

        下面就#pragma平常用的比较多的用法讲解一下。

2.#pragma once

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

//使用pragma once
#pragma once

它的功能相当于: 

//使用宏定义方式
#ifndef _MY_TEST_HEADER_H_
#define _MY_TEST_HEADER_H_#endif // !_MY_TEST_HEADER_H_

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

#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 )

4.#pragma comment

4.1.lib

用于指导编译器进行库的链接,以确保程序能够正确地与外部库进行链接。这在开发大型项目时尤为重要。通过#pragma comment指令,我们可以告诉编译器要链接的库的名称。如:

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

4.2.linker

在目标文件中放置连接程序选项。你可以用这个描述类型指定连接程序选项来代替在Project Setting对话框中Link页内的选项。例如,你可以指定/include选项以强迫包含一个符号:

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

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这一行的左边会出现一个“-”号,单击以折叠内容,同时“-”号会变成“+”号,再次单击可以展开代码块。

6.#pragma optimize

首先,让我们来了解一下如何通过Pragma指令对代码进行优化。在C++编程中,我们经常需要追求代码的高效性和性能。Pragma提供了一系列指令,可以告诉编译器如何对代码进行优化。例如,#pragma optimize指令可以告诉编译器在编译过程中对代码进行优化,以达到更好的执行效果。如:

#pragma optimize(3, on)#pragma optimize("", off)  
// 一些不需要优化的代码  
#pragma optimize("", on)

7.#pragma message (message string)

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

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

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

#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")   //注意把行号转成了字符串

8.#pragma omp parallel for

OpenMP并行指令,OpenMP(Open Multi-Processing)是一套用于并行编程的API,而#pragma omp指令就是用来指导编译器进行OpenMP并行化的。通过在循环、函数等代码块前加上#pragma omp,可以让编译器自动并行化该代码块,充分利用多核处理器的性能。

#pragma omp parallel for
for (int i = 0; i < n; ++i) {// 并行化的代码
}

9.#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 .

  9. #pragma pack(pop, identifier) 将编译栈栈中标识为 identifier 位置的值弹出, 并将其设为当前值. 注意, 如果栈中所标识的位置之上还有值, 那会先被弹出并丢弃.

  10. #pragma pack(push, identifier, n) 将当前字节对齐值压入编译栈栈顶, 然后将栈中保存该值的位置标识为 identifier, 再将 n 设为当前值.

  11. #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。

10.#pragma alias(…)

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

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

要搜索的别名必须完全符合规范,无论是大小写、拼写还是双引号或尖括号的使用。 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>  

11.#pragma code_seg

语法:

#pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )

该指令用来指定函数在.obj文件中存放的节,观察OBJ文件可以使用VC自带的dumpbin命令行程序,函数在.obj文件中默认的存放节为.text节,如果code_seg没有带参数的话,则函数存放在.text节中。
        push (可选参数) 将一个记录放到内部编译器的堆栈中,可选参数可以为一个标识符或者节名
        pop(可选参数) 将一个记录从堆栈顶端弹出,该记录可以为一个标识符或者节名
        identifier (可选参数) 当使用push指令时,为压入堆栈的记录指派的一个标识符,当该标识符被删除的时候和其相关的堆栈中的记录将被弹出堆栈
        "segment-name" (可选参数) 表示函数存放的节名
例如:

//默认情况下,函数被存放在.text节中
void func1() {                   // stored in .text
}
//将函数存放在.my_data1节中
#pragma code_seg(".my_data1")
void func2() {                   // stored in my_data1
}
//r1为标识符,将函数放入.my_data2节中
#pragma code_seg(push, r1, ".my_data2")
void func3() {                   // stored in my_data2
}
int main() {
}

12.总结

        总的来说,#pragma 是一个强大的工具,但应该谨慎使用,确保它不会导致代码在不同编译器或平台上的不可移植性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/757190.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

51单片机—AD/DA

目录 1.AD/DA元件介绍 2.运算放大器 3.原理分析 4.性能指标 5.内部芯片时序 6.程序实操 7.原理图 1.AD/DA元件介绍 一般传感器的值会随参数的值变化&#xff0c;AD一般对电压进行转换&#xff0c;AD有多个通道&#xff0c;但是DA只有一个通道&#xff0c;且AD的运用较为广…

路由器怎么做端口映射

路由器在网络中起到了连接不同设备和提供网络服务的重要作用。端口映射是一项常见的操作&#xff0c;它允许外部网络中的设备通过路由器访问内部网络中的设备。我们将介绍如何在路由器上进行端口映射的设置。 理解端口映射 在开始操作之前&#xff0c;我们需要了解一些基本概念…

反射 Reflection

反射 反射的概念 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量&#xff0c;构造器&#xff0c;成员方法等等)&#xff0c;并能操作对象的属性及方法。反射在设计模式和框架底层都会用到加载完类之后&#xff0c;在堆中就产生了一个Class类型…

前端之各浏览器间差异和平台的兼容性问题

目录 这些差异和兼容性问题主要源于以下方面为解决这些兼容性问题&#xff0c;常用的做法包括浏览器的差异主要体现在它们对于 HTML、CSS 和 JavaScript 的解析和渲染上。为了处理不同浏览器和平台的兼容性问题&#xff0c;我们可以采取以下几个步骤&#xff1a;IE、Firefox、S…

GET请求和POST请求

GET请求和POST请求是HTTP协议中最常用的两种请求方法&#xff0c;它们在Web开发中起着至关重要的作用。 GET请求 用途&#xff1a;用于请求指定资源的表示形式&#xff0c;通常用于获取数据。幂等性&#xff1a;GET请求是幂等的&#xff0c;即多次请求同一资源应该返回相同的…

Flutter 在 Windows 下的开发环境搭建(Flutter SDK 3.19.2)【图文详细教程】

Git 下载与安装 对于 Flutter 3.19&#xff0c;Git 版本需要 2.27 及以上 Git 下载&#xff1a; Git 官网&#xff1a;https://git-scm.com/Git 下载淘宝镜像&#xff1a;https://registry.npmmirror.com/binary.html?pathgit-for-windows/ 对于 Git 的安装教程&#xff0c;…

Python分析无人驾驶汽车在桂林市文旅行业推广的问卷

【项目背景】 通过市场调研、文本分析、访谈和问卷调查等方法&#xff0c;探讨&#xff1a; 网民对无人驾驶汽车出行服务的态度。无人驾驶安全员的行业背景。不同人群在旅游时的交通选择偏好。游客及当地居民对桂林市文旅路线的交通满意度。乘客对无人驾驶汽车的满意度。桂林…

超快速排序(蓝桥杯,归并排序,acwing)

题目描述&#xff1a; 在这个问题中&#xff0c;您必须分析特定的排序算法----超快速排序。 该算法通过交换两个相邻的序列元素来处理 n 个不同整数的序列&#xff0c;直到序列按升序排序。 对于输入序列 9 1 0 5 4&#xff0c;超快速排序生成输出 0 1 4 5 9。 您的任务是确…

Selenium不同版本配置自动下载驱动及打包细节

Selenium配置浏览器驱动 自动下载浏览器驱动的方法 selenium4.7.0自动下载浏览器驱动的方法 selenium4.11.0 或4.11.1手动设置浏览器驱动路径的方法pyinstaller打包程序时同时打包ChromeDriverchromedriver路径需要sys._MEIPASS的路径进行引用方法一&#xff1a;通过–add-data…

【Linux】从零开始认识进程 — 前篇

我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。。——山本耀司 从零开始认识进程 1 认识冯诺依曼体系2 操作系统3 进程3.1 什么是进程&#xff1f;&#xff1f;&#xff1f;3.2 进程管理PCB 3.3 Linux中的进程深入理解 3.4 进程创建总结 送给…

publicPath 和 __webpack_public_path__ 和 process.env.BASE_URL的区别和使用方法

文章目录 publicPath 和 __webpack_public_path__ 和 process.env.BASE_URL的区别和使用方法1. publicPath&#xff08;1&#xff09;publicPath定义&#xff08;2&#xff09;publicPath使用方法方法1&#xff1a;静态文件使用publicPath。方法2&#xff1a;项目打包时使用pub…

jvm的垃圾回收器以及触发full gc的场景

JVM&#xff08;Java虚拟机&#xff09;的垃圾回收器有很多种&#xff0c;主要包括以下几种&#xff1a; Serial收集器&#xff1a;串行收集器是最古老、最稳定的收集器。它使用单个线程进行垃圾收集工作&#xff0c;在进行垃圾回收时会暂停所有用户线程。 ParNew收集器&#…

Nebula Graph-01-Nebula Graph简介和安装以及客户端连接

前言 NoSQL 数据库 图数据库并不是可以克服关系型数据库缺点的唯一替代方案。现在市面上还有很多非关系型数据库的产品&#xff0c;这些产品都可以叫做 NoSQL。NoSQL 一词最早于上世纪 90 年代末提出&#xff0c;可以解释为“非 SQL” 或“不仅是 SQL”&#xff0c;具体解释要…

使用flatten-maven-plugin时更换版本号步骤

flatten-maven-plugin作用和配置 略 第一步: 执行mvn命令,更新.flattened-pom.xml mvn clean package -Drevision"1.0.1-snapshot" 版本号如果有-,则必须加上双引号 第二步: 修改POM文件中版本号reversion 第三步: reload all maven projects

查找众数及中位数 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 众数是指一组数据中出现次数量多的那个数&#xff0c;众数可以是多个。 中位数只是指把一组数据从小到大排列&#xff0c;最中间的那个数&#xff0c;如果这组数…

整蛊小教程|让朋友手足无措的电脑自动关机

前言 这几天讲到shutdown关机命令&#xff0c;于是就出现了整蛊类的电脑教程。 这个故事我记得很清楚&#xff1a;在2012年的春天……当时的小白对电脑还不是很熟悉。某一天跟着朋友去网吧上网&#xff0c;这时候突然有个朋友发来一个.bat的文件&#xff0c;说双击打开有惊喜…

单片机烧录方式,JTAG,ISP,SWD,

常见的词汇 参考 ISP&#xff1a;In System Programing&#xff0c;在系统编程 IAP&#xff1a;In Application Programing&#xff0c;在应用编程 ICP&#xff1a;In Circuit Programing&#xff0c;在电路编程 ICSP全称是In Circuit Serial Programming JTAG(Joint Test Act…

集合深入------理解底层。

集合的使用 前提&#xff1a;栈、堆、二叉树、hashcode、toString()、quesalus()的知识深入和底层理解。 1、什么是集合 集合就是咋们所说的容器 ​ 前面我们学习过数组 数组也是容器 ​ 容器&#xff1a;装东西的 生活中有多少的容器呀? 水杯 教室 酒瓶 水库 只要是…

Modbus TCP转Profinet网关如何实现Modbus主站与多设备通讯

在工业控制领域中&#xff0c;Modbus TCP转Profinet网关&#xff08;XD-ETHPN20&#xff09;扮演着连接不同设备间通讯的重要角色。当将Modbus主站与十几台服务器进行通讯时&#xff0c;通过modbus tcp转profinet网关&#xff08;XD-ETHPN20&#xff09;设备将不同协议间的数据…

[LLM]大模型八股知识点(一)

基础面 1 目前主流的开源模型体系有哪些&#xff1f; 目前主流的开源LLM&#xff08;语言模型&#xff09;模型体系包括以下几个&#xff1a; GPT&#xff08;Generative Pre-trained Transformer&#xff09;系列&#xff1a;由OpenAI发布的一系列基于Transformer架构的语言…