what is #pragma in c

#Pragma预处理指令可用于设定编译器状态或指示特定动作。本文介绍多种实用参数,如message参数用于输出编译信息,code_seg参数指定代码段位置,pragma once确保头文件仅被包含一次等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#pragma 预处理指令详解[转载]

在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C ++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
其格式一般为: #Pragma Para
其中Para 为参数,下面来看一些常用的参数。


(1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗
口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:
#Pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_
X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了


(2)另一个使用得比较多的pragma参数是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。

(3)#pragma once (比较常用)
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。

(4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。

(5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体
外观的定义。

(6)#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 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1---4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
常用的lib关键字,可以帮我们连入一个库文件。
(8)用pragma导出dll中的函数

    传统的到出 DLL 函数的方法是使用模块定义文件 (.def),Visual C++ 提供了更简洁方便的方法,那就是“__declspec()”关键字后面跟“dllexport”,告诉连接去要导出这个函数,例如:

__declspec(dllexport) int __stdcall MyExportFunction(int iTest);

    把“__declspec(dllexport)”放在函数声明的最前面,连接生成的 DLL 就会导出函数“_MyExportFunction@4”。

    上面的导出函数的名称也许不是我的希望的,我们希望导出的是原版的“MyExportFunction”。还好,VC 提供了一个预处理指示符“#pragma”来指定连接选项 (不仅仅是这一个功能,还有很多指示功能) ,如下:

#pragma comment(linker,"/EXPORT:MyExportFunction=_MyExportFunction@4")

    这下就天如人愿了:)。如果你想指定导出的顺序,或者只将函数导出为序号,没有 Entryname,这个预处理指示符 (确切地说是连接器) 都能够实现,看看 MSDN 的语法说明:

/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]

   @ordinal 指定顺序;NONAME 指定只将函数导出为序号;DATA 关键字指定导出项为数据项。



每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。例如,对循环优化功能:
#pragma loop_opt(on) // 激活
#pragma loop_opt(off) // 终止
有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,如“Parameter xxx is never used in function xxx”,可以这样:
#pragma warn —100 // Turn off the warning message for warning #100
int insert_record(REC *r)
{ /* function body */ }
#pragma warn +100 // Turn the warning message for warning #100 back on
函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。
每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。

#ifndef _INI_DOCUMENT_H_ #define _INI_DOCUMENT_H_ #define ASCIILINESZ 1024 //每行文本能容纳文字的最大长度 #define ASCIISECTIONSZ 256 //每个Section的最大长度 #define ASCIIKEYSZ 256 //每个Key的最大长度 #define ASCIIVALUESZ 256 //每个Value的最大长度 #define ASCIIDESCSZ 512 //每个注释的最大长度 #include "RBTree.h" #include "Section.h" #include "Key.h" #include "Value.h" namespace INI { using KeyMap = RBTREE::RBTree<Key, Value>; using SectionMap = RBTREE::RBTree<Section, KeyMap>; class IniDocument { typedef enum class _line_status_ : unsigned char { LINE_ERROR,//未知行 LINE_DESCRIPTION,//注释行 LINE_SECTION,//节点行 LINE_KEY//键值行 } line_status; public: IniDocument(const char* _szFile); ~IniDocument(); KeyMap& operator[](const char* _szSection); void Clear(); private: void load(); line_status parse(char* _szLine, char* _szSection, char* _szKey, char* _szValue, char* _szDesc); private: char* m_szFile;//文件路径 SectionMap m_sectionMap; //存储section的映射 }; } #endif // !_INI_DOCUMENT_H_ #include "../inc/IniDocument.h" INI::IniDocument::IniDocument(const char* _szFile) : m_szFile(nullptr) { if (!_szFile || _szFile[0] == '\0') { //错误处理: 文件路径不能为空 throw("文件路径不能为空."); } size_t nLen = strlen(_szFile); size_t nSize = ((nLen + 1) + 3) & ~3; if (nLen >= _MAX_PATH) { //错误处理: 文件路径过长 throw("文件路径过长."); } m_szFile = new char[nSize]; memcpy(m_szFile, _szFile, nLen + 1); load(); //加载文件内容到内存 } INI::IniDocument::~IniDocument() { Clear(); } INI::KeyMap& INI::IniDocument::operator[](const char* _szSection) { //返回对应Section的映射 return m_sectionMap[Section(_szSection)]; } void INI::IniDocument::Clear() { if (m_szFile) { delete[] m_szFile; m_szFile = nullptr; } for (SectionMap::iterator it = m_sectionMap.begin(); it != m_sectionMap.end(); ++it) { //清空每个Section的映射 it->second.Clear(); } m_sectionMap.Clear(); //清空Section映射 } void INI::IniDocument::load() { /*特别注意 读出的ini内容一般都是utf-8,而保存在内存中的内容也是utf-8.直接保存到文件没问题 但如果查找就会出问题。要把查找的中文字串 转成utf8再查找才能找到 但每次查找section或者key都转一次utf8就浪费了 解决方案: 从文件解析出来的utf8内容转回普通char字串。再保存到内存中,这样代码直接就能查找到。 而保存回文件的时候,把内存的字串转回utf8再保存即可 如果不想频繁转换,直接使用ANSI编码文件即可 */ #pragma warning(disable:4996) FILE* pFile = fopen(m_szFile, "r"); #pragma warning(default:4996) if (!pFile) { printf("<IniDocument::load> 打开Ini文件失败,路径: %s\n", m_szFile); return; } char szLineBuffer[ASCIILINESZ] = { 0 };//把文件中的内容逐行读入此内存中 char szSection[ASCIISECTIONSZ] = { 0 };;//把节点解析完成后放入此内存中 char szKey[ASCIIKEYSZ] = { 0 };;//把Key解析完成后放入此内存中 char szValue[ASCIIVALUESZ] = { 0 };;//把Value解析完成后放入此内存中 char szDesc[ASCIIDESCSZ] = { 0 };;//把注释读入此内存中 size_t nLines = 0; //记录当前的行数 size_t nLen = 0; //记录当前行的长度 KeyMap* pKeyMap = nullptr; //用于存储当前解析的Key映射 Desc desc; //用于存储当前解析的全行注释 while (fgets(szLineBuffer, ASCIILINESZ, pFile))//每次读取一行内容 { ++nLines; //行数自增 if (szLineBuffer[0] == '\n')continue;//如果取出来的内容第一个字符就是换行符,无条件取下一行内容 nLen = strlen(szLineBuffer);//更新行内容的字串长度 if (szLineBuffer[nLen - 1] != '\n' && !feof(pFile)) { //如果此行的结束符 不是换行符\n 并且 文件并未结束 -> 如果都满足条件,说明此行肯定是太长了。应当调整行的最大长度。默认:ASCIILINESZ=1024 printf("<IniDocument::load> 行 %zu 内容过长,请检查文件内容.\n", nLines); continue; //跳过此行 } //开始进行字串解析 switch (parse(szLineBuffer, szSection, szKey, szValue, szDesc))//进入解析过程 { case line_status::LINE_ERROR://未知行 break; case line_status::LINE_DESCRIPTION://全行注释 { desc = szLineBuffer; //将注释内容存储到Desc对象中 break; } case line_status::LINE_SECTION://节点 { Section section(szSection); //创建Section对象 if (desc.m_szDesc)section.m_vecDesc[0] = std::move(desc); //将注释内容存储到Section对象中 if (szDesc[0] != '\0')section.m_vecDesc[1] = szDesc; //如果有第二个注释内容,存储到Section对象中 pKeyMap = &m_sectionMap[section]; //获取当前Section映射 break; } case line_status::LINE_KEY://键值 { if (!pKeyMap) { printf("<IniDocument::load> 键值行 %zu 解析失败<没有Section>,请检查文件内容.\n", nLines); continue; //如果没有Section映射,跳过此行 } Key key(szKey); //创建Key对象 Value value(szValue); //创建Value对象 if (desc.m_szDesc) //如果有全行注释内容 { //将注释内容存储到Key对象中 key.m_vecDesc[0] = std::move(desc); } if (szDesc[0] != '\0') //如果有行块注释内容 { key.m_vecDesc[1] = szDesc; //将行块注释内容存储到Key对象中 } (*pKeyMap)[key] = std::move(value); //将Key和Value存储到当前Section映射中 break; } default: break; } szLineBuffer[0] = 0; //清空行内容 szSection[0] = 0; //清空Section内容 szKey[0] = 0; //清空Key内容 szValue[0] = 0; //清空Value内容 szDesc[0] = 0; //清空注释内容 } fclose(pFile); //关闭文件 } INI::IniDocument::line_status INI::IniDocument::parse(char* _szLine, char* _szSection, char* _szKey, char* _szValue, char* _szDesc) { #pragma warning(disable:4996) line_status sta = line_status::LINE_ERROR;//分析结果 size_t nCount = 0; //记录sccanf匹配的数量 if (_szLine[0] == '#' || _szLine[0] == ';')//开头就是单行注释的解析符 {//说明全行都是注释 sta = line_status::LINE_DESCRIPTION; } //开始解析节点 Section //(必须遵守一个规则,从有到无,比如结尾带分号的应该先做匹配。 //然后再到结尾不带分号的。因为如果先匹配不带分号的,带分号的也会匹配得上。就会莫名多个分号了) //例如: key = value; 如果先匹配无分号的,那么值就是 value; 达不到预期效果,结尾多了个分号 else if ( //以下匹配 节点(后面带注释) (nCount = sscanf(_szLine, "[%[^]]]%[^\n]", _szSection, _szDesc)) == 2 ||//例子: [section01]#this is description 01或者[section01];this is description 01 (nCount = sscanf(_szLine, "[%[^]]]%[^\n]", _szSection, _szDesc)) == 1//例子: [section] ) { sta = line_status::LINE_SECTION; } //开始解析键值 key = value else if ( //(必须遵守一个规则,从有到无,比如结尾带分号的应该先做匹配。然后再到结尾不带分号的。因为如果先匹配不带分号的,带分号的也会匹配得上。就会莫名多个分号了) (nCount = sscanf(_szLine, "%[^=]=%[^;]%[^\n]", _szKey, _szValue, _szDesc)) == 3 || //例子: key1=value1;this is description 01 (nCount = sscanf(_szLine, "%[^=]=%[^#]%[^\n]", _szKey, _szValue, _szDesc)) == 3 ||//例子: key2=value2#this is description 01 (nCount = sscanf(_szLine, "%[^=]=%[^\n]", _szKey, _szValue)) == 2//例子: key1=value1 ) { sta = line_status::LINE_KEY; } #pragma warning(default:4996) return sta; } 把我提供给你的所有代码再结合这次给你的代码,你可以尝试重构组合,是否已经形成一个完整的ini解析器
最新发布
07-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值