😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 C语言预处理的 #pragma 指令🍭
😎金句分享😎:🍭🍭
本文未经允许,不得转发!!!
目录
- 🎄一、概述
- 🎄二、#pragma 指令
- ✨2.1 #pragma message
- ✨2.2 #pragma code_seg
- ✨2.3 #pragma once
- ✨2.4 #pragma warning
- ✨2.5 #pragma comment
- 🎄三、#pragma pack
- ✨3.1 内存对齐是什么?
- ✨3.2 为什么需要内存对齐?
- ✨3.3 复杂结构体大小计算
- 🎄四
- 🎄五
- 🎄六、
🎄一、概述
上篇文章讲了C语言预处理的 #define、#under、#if、#else、#elif、#endif、#include、#error
的预处理指令。这篇文章接着介绍C语言预处理的其他指令。
预处理名称 | 意 义 |
---|---|
#define | 宏定义 |
#undef | 撤销已定义过的宏名 |
#include | 使编译程序将另一源文件嵌入到带有#include 的源文件中 |
#if | #if 的一般含义是如果#if 后面的常量表达式为 true, 则编译它与#endif 之间的代码,否则跳过这些代码。 命令#endif 标识一个#if 块的结束。 #else命令的功能有点象 C 语言中的 else , #else 建立另一选择(在# if 失败的情况下)。 #elif 命令意义与 else if 相同,它形成一个 if else-if 阶梯状语句,可进行多种编译选择。 |
#else | |
#elif | |
#endif | |
#ifdef | 用#ifdef 与#ifndef 命令分别表示“如果有定义”及“如果无定义”,是条件编译的另一种方法。 |
#ifndef | |
#line | 改变当前行数和文件名称,它们是在编译程序中预先定义的标识符命令的基本形式如下: #line number["filename"] |
#error | 编译程序时,只要遇到 #error 就会生成一个编译错误提示消息,并停止编译 |
#pragma | 为实现时定义的命令,它允许向编译程序传送各种指令例如,编译程序可能有一种选择,它支持对程序执行的跟踪。可用#pragma 语句指定一个跟踪选择。 |
🎄二、#pragma 指令
在所有的预处理指令中, #pragma
指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。
其格式一般为:
#pragma para
其中 para
为参数,下面来看一些常用的参数。
✨2.1 #pragma message
message
参数能够在编译信息输出窗口中输出相应的信息,其使用方法:
#pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
假设我们希望判断自己有没有在源代码的什么地方定义了_X86 这个宏可以用下面的方法:
#ifdef _X86
#pragma message(“_X86 macro activated!”)
#endif
✨2.2 #pragma code_seg
code_seg
参数能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。其格式如下:
#pragma code_seg( ["section-name"[,"section-class"] ] )
✨2.3 #pragma once
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次。
下面是websocket项目的一个头文件,里面就有#pragma once
的使用:
// Copyright Vladimir Prus 2002.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)// See www.boost.org/libs/program_options for documentation.#ifndef PROGRAM_OPTIONS_VP_2003_05_19
#define PROGRAM_OPTIONS_VP_2003_05_19#if defined(_MSC_VER)
#pragma once
#endif#include <boost/program_options/options_description.hpp>
#include <boost/program_options/positional_options.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/option.hpp>
#include <boost/program_options/value_semantic.hpp>
#include <boost/program_options/version.hpp>#endif
✨2.4 #pragma warning
#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等价于:
#pragma warning(disable:4507 34) // 不显示 4507 和 34 号警告信息
#pragma warning(once:4385) // 4385 号警告信息仅报告一次
#pragma warning(error:164) // 把 164 号警告信息作为一个错误。
同时这个 pragma warning 也支持如下格式,这里 n 代表一个警告等级(1—4)。:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
#pragma warning( push )
:保存所有警告信息的现有的警告状态。
#pragma warning( push, n)
:保存所有警告信息的现有的警告状态,并且把全局警告等级设定为 n。
#pragma warning( pop )
:向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
✨2.5 #pragma comment
#pragma comment
指令将一个注释记录放入一个对象文件或可执行文件中,格式如下:
#pragma comment(...)
常用的 lib
关键字,可以帮我们链入一个库文件。 比如:
#pragma comment(lib, "user32.lib") // 该指令用来将 user32.lib 库文件加入到本工程中。
linker
关键字将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中设置的链接选项,你可以指定/include 选项来强制包含某个对象,例如:
#pragma comment(linker, "/include:__mySymbol")
下面是websocket项目源码的截取:
# if !defined(BOOST_ASIO_NO_DEFAULT_LINKED_LIBS)
# if defined(UNDER_CE)
# pragma comment(lib, "ws2.lib")
# elif defined(_MSC_VER) || defined(__BORLANDC__)
# pragma comment(lib, "ws2_32.lib")
# if !defined(BOOST_ASIO_WINDOWS_APP)
# pragma comment(lib, "mswsock.lib")
# endif // !defined(BOOST_ASIO_WINDOWS_APP)
# endif // defined(_MSC_VER) || defined(__BORLANDC__)
# endif // !defined(BOOST_ASIO_NO_DEFAULT_LINKED_LIBS)
🎄三、#pragma pack
#pragma pack 指令是本文的重头戏,所以单独用一小节来介绍。在介绍这个关键字之前,先认识一下内存对齐
。
✨3.1 内存对齐是什么?
内存对齐(Memory Alignment) 是指将数据存储在内存中时,是按照特定的规则将数据放置在地址为其大小倍数的位置上。具体而言,内存对齐要求变量的起始地址是它自身大小的整数倍
。
看下面这两个结构体,看看按照内存对齐的要求,是怎么存储的:
struct Test1
{char c1;short s;char c2;int i;
};struct Test2
{char c1;char c2;short s;int i;
};
两个结构体虽然结构体成员一样,但他们所占用的内存大小却不一样,Test1占用12个字节,Test2占用8个字节,他们在内存中的存储大致如下图:
看看struct Test1
按照内存对齐要求是怎样安排的:
- 成员
c1
自身大小为 1 个字节,是结构体第一成员,所以直接在地址0
; - 成员
s
自身大小为 2 个字节,按照要求,其起始地址必须是 2 的整数倍,所以不能放在地址1
的位置,起始地址为地址2
; - 成员
c1
自身大小为 1 个字节,起始地址需要为 1 的整数倍,直接安排在地址4
; - 成员
i
自身大小为 4 个字节,起始地址需要为 4 的整数倍,从地址5
开始往后找,地址8
是4的整数倍。
✨3.2 为什么需要内存对齐?
计算机处理器为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。
所以,内存对齐的主要目的是提高访问数据的效率。当数据按照规定的对齐方式存放在内存中时,处理器可以更快地读取和存储数据,而不需要执行额外的内存操作。未对齐的数据可能导致性能下降,甚至在某些架构中导致程序崩溃。
✨3.3 复杂结构体大小计算
按照内存对齐
的要求,结构体在内存中是怎么存储的?这小节介绍复杂结构体各个成员在内存中怎么存储?这里的复杂结构体是指包含了结构体和数组的结构体。先看看下面结构体ST_2
,你能清除它各个成员的在结构体中的偏移量吗?
typedef struct st
{char c;int i;double d;
}ST;typedef struct st2
{char c; int i; ST st; double d; char c2; ST st_arr[3];long l; int i_arr[9];
}ST_2;
🎄四
🎄五
🎄六、
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁