您使用#pragma编写的代码有什么用?

Xav*_* Ho 22 c++ compiler-construction pragma

我从来没有理解#pragma once什么时候#ifndef #define #endif总是有效的需要.

我已经看到了#pragma comment与其他文件链接的用法,但使用IDE可以更轻松地设置编译器设置.

有哪些其他用法#pragma是有用的,但并不广为人知?

编辑:

只是#pragma指令列表之后.也许我应该再多说一下这个问题:

你和你一起写的代码#pragma有用吗?

.

答案一目了然:

感谢所有回答和/或评论的人.以下是我发现有用的一些输入的摘要:

  • Jason建议使用#pragma once#ifndef #define #endif允许更快地编译大型系统.史蒂夫跳进来支持这一点.
  • 280Z28领先并提到#pragma onceMSVC首选,而GCC编译器优化#ifndef #define #endif.因此,应该使用其中之一,而不是两者.
  • Jason还提到#pragma pack了二进制兼容性,而Clifford则反对这一点,因为可移植性和字节序可能存在问题.Evan提供了一个示例代码,Dennis告知大多数编译器会强制执行填充以进行对齐.
  • sblom建议#pragma warning用来隔离真正的问题,并禁用已经审查过的警告.
  • Evan建议#pragma comment(lib, header)在不重新设置IDE的情况下轻松移植项目.当然,这不太便携.
  • sbi #pragma message为VC用户提供了一个很好的技巧来输出带有行号信息的消息.詹姆斯更进一步,允许errorwarning匹配MSVC的消息,并将适当显示,如错误列表.
  • Chris提供#pragma region了能够在MSVC中使用自定义消息折叠代码.

哇,等等,如果我想发布使用#pragmas除非必要,该怎么办?

  • 克利福德从另一个角度发布了关于使用的观点#pragma.荣誉.

如果SOers感觉到发布答案的冲动,我会在此列表中添加更多内容.感谢大家!

Jas*_*ams 12

每个pragma都有它的用途,或者它们首先不存在.

如果你知道你不会将代码移植到不同的编译器,那么pragma"once"只是更少打字和更整洁.它应该更高效,因为编译器根本不需要解析头来确定是否包含其内容.

编辑:回答评论:想象你有一个200kB的头文件.使用"once",编译器加载一次,然后知道它不需要在下次看到引用时包含头.使用#if,它必须每次加载和解析整个文件,以确定if禁用所有代码,因为每次都必须评估if.在大型代码库上,这可能会产生显着差异,但实际上(特别是对于预编译的头文件)它可能不会.

当你需要结构的二进制兼容性时,pragma"pack"是非常有用的.

编辑:对于二进制格式,您提供的字节必须与所需格式完全匹配 - 如果您的编译器添加了一些填充,它将搞砸数据对齐并破坏数据.因此,对于希望传递给OS调用或TCP数据包的二进制文件格式或内存结构的序列化,使用直接映射到二进制格式的结构比"成员序列化"更有效(逐个编写字段) - 它使用更少的代码并运行得更快(在嵌入式应用程序中至关重要,即使在今天).

编译指示"错误"和"消息"非常方便,特别是在条件编译块内(例如"错误:'发布ePhone'版本未实现",消息:"在此版本中启用了额外的调试和分析代码")

编译指示"警告"(特别是使用push和pop)对于暂时禁用恼人的警告非常有用,尤其是当包含写得不好的第三方标题(充满警告)时 - 特别是如果您使用警告级别4构建.

编辑:良好的做法是在构建中实现零警告,以便在发生警告时您注意到并立即修复它.您当然应该在自己的代码中修复所有警告.但是,有些警告根本无法修复,也没有告诉你任何重要的事情.此外,在使用第三方库时,如果您无法更改其代码以修复警告,则可以通过禁用库的警告来删除构建中的"垃圾邮件".使用push/pop允许您仅在库包含期间有选择地禁用警告,以便编译器仍然检查您自己的代码.

  • "#if它必须每次都加载并解析整个文件,以确定所有代码都被if禁用,因为每次都必须评估if".这不是真的.海湾合作委员会有一个优化,以发现头部包括警卫,并对待他们大致相同,如果警卫是外部的.在C++标准中没有要求预处理器实际上经历了读取文件的动作,只是整个编译产生了正确的程序.当然,对于缺乏此优化但只有#pragma一次的编译器,该pragma提供了相同的好处. (4认同)

Eva*_*ran 9

正如你所提到的,我在visual c ++中看过pragma,告诉它在链接时链接到某个库.方便的库需要winsock库.这样,您无需修改​​项目设置即可将其链接.例如: #pragma comment(lib,"wsock32.lib").我喜欢这个,因为它将需要.lib的代码与它相关联,再加上你把它放在文件中,如果你在另一个项目中重用那个代码,你就不会忘记它.

此外,用于打包数据结构的编译指示通常对于数据成员的偏移很重要的系统和网络编程非常有用.例如:

#pragma pack(push, 1) // packing is now 1
struct T {
char a;
int b;
};
#pragma pack(pop) // packing is back to what it was

// sizeof(T) == sizeof(char) + sizeof(int), normally there would be padding between a and b
Run Code Online (Sandbox Code Playgroud)

  • +1.pragma lib非常*非常有用. (4认同)

Cli*_*ord 9

你应该尽可能避免使用#pragma.#pragma编译器指令始终是特定于编译器的,因此是不可移植的.它们应该被视为最后的手段.

此外,遇到无法识别的pragma的编译器的ISO要求行为是简单地忽略它.这可以在没有警告的情况下静默执行,因此如果指令对于代码的正确操作至关重要,它可能会编译但在使用不同的编译器编译时无法按预期运行.GCC的例子使用了非常少的语法以及主要只针对特定目标的编译器的行为或兼容性与一些其他的编译器.因此,如果您想确保可移植性,最终会得到以下构造:

#if _MSC_VER
  #pragma PACK(push,1)
#elif   __GNUC__
  // nothing to do here
#else
  #error "Unsupported compiler"
#endif
  struct sPackedExample
  {
      // Packed structure members
#if _MSC_VER
  } ;                              // End of MSVC++ pragma packed structure
  #pragma pack (pop)
#elif   __GNUC__
  }__attribute__((__packed__)) ;   // End of GNU attribute packed structure
#endif
Run Code Online (Sandbox Code Playgroud)

这是一个混乱,你很快就看不到树木的木材,并且当你添加对更多编译器的支持时问题变得更糟(这反过来需要知道识别编译器的预定义宏.

[注意:] GCC 4.x确实支持#pragma pack以实现MS兼容性,因此上面的例子有点人为,但对于可能仍在使用的早期版本的GCC或其他编译器来说并非如此.

'#pragma once'特别成问题,因为对于不支持它的编译器,除了最简单的情况之外,代码将在预处理时中断.应该首选更详细但便携的解决方案.Visual C++的应用程序和代码生成'向导'可能会使用它,但在任何情况下,这些代码通常都是不可移植的.您应该知道在使用此类代码时,您实际上是将项目锁定到Microsoft的工具中.这可能不是问题,但我不建议在您自己的代码中使用该指令.

为了解决您的原始问题:" 您使用#pragma编写的代码有什么用? "; 你应该考虑避免使用pragma的有用方法吗?

它不应该是" 有用 " 的问题,而是" 必要性 ".例如,我使用过的许多嵌入式系统编译器,使用#pragma指令来指定函数是一个中断服务例程,因此具有不同的进入/退出代码,并且在许多情况下在不同的堆栈上运行.避免使用这种编译指示需要了解目标的汇编语言,并且在调用C代码来处理中断时效率会降低.


Jam*_*lis 8

这与sbi的答案非常相似,但有一些额外的功能.

我已经#pragma message在Microsoft Visual C++上使用了以下一组宏了一段时间:

#define EMIT_COMPILER_WARNING_STRINGIFY0(x) #x
#define EMIT_COMPILER_WARNING_STRINGIFY1(x) EMIT_COMPILER_WARNING_STRINGIFY0(x)
#define EMIT_COMPILER_MESSAGE_PREFACE(type) \
    __FILE__ "(" EMIT_COMPILER_WARNING_STRINGIFY1(__LINE__) "): " type ": "

#define EMIT_COMPILER_MESSAGE EMIT_COMPILER_MESSAGE_PREFACE("message")
#define EMIT_COMPILER_WARNING EMIT_COMPILER_MESSAGE_PREFACE("warning")
#define EMIT_COMPILER_ERROR   EMIT_COMPILER_MESSAGE_PREFACE("error")
Run Code Online (Sandbox Code Playgroud)

用作:

#pragma message(EMIT_COMPILER_WARNING "This code sucks; come back and fix it")
Run Code Online (Sandbox Code Playgroud)

这会在构建输出中生成以下文本:

1>z:\sandbox\test.cpp(163): warning : This code sucks; come back and fix it
Run Code Online (Sandbox Code Playgroud)

输出与Visual C++错误消息格式匹配,因此错误列表中会显示错误,警告和消息以及所有其他编译器警告和错误.

"警告"宏比// todo fix this代码中的简单宏更令人讨厌,并帮助我记住回来修复一些东西.

"错误"宏很有用,因为它会导致编译失败,但不会像#error指令那样立即停止编译过程.