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

时间:2010-04-24 06:52:11

标签: c++ compiler-construction pragma

#ifndef #define #endif始终有效时,我从未理解#pragma once的必要性。

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

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

修改

我只是在#pragma指令列表之后而不是。也许我应该再多说一遍这个问题:

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

答案一览:

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

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

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

  • Clifford从另一个角度发布了 not 来使用#pragma。奖励。

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

8 个答案:

答案 0 :(得分:12)

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

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

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

当你需要结构的二进制兼容性时,

pragma“pack”是非常有用的。

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

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

pragma“警告”(特别是使用push&amp; pop)对暂时禁用恼人的警告非常有用,特别是当包含写得不好的第三方标题(充满警告)时 - 特别是如果您使用警告级别4构建。

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

答案 1 :(得分: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

答案 2 :(得分:8)

你应尽可能避免使用#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

这是一团糟,你很快就看不到树木的问题,而且当你添加对更多编译器的支持时,问题会变得更糟(这反过来需要知道pre-defined macros识别编译器。

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

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

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

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

答案 3 :(得分:8)

这与sbi's answer非常相似,但有一些额外的功能。

我在Microsoft Visual C ++上使用了以下一组带有#pragma message的宏:

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

用作:

#pragma message(EMIT_COMPILER_WARNING "This code sucks; come back and fix it")

导致构建输出中的以下文本:

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

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

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

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

答案 4 :(得分:4)

在Visual Studio中,C ++预处理器也支持

#pragma region Some Message Goes Here
...
#pragma endregion

然后,您可以在代码编辑器中折叠此区域,以便它仅显示上述消息。这类似于C#区域语法。

答案 5 :(得分:3)

根据定义,

#pragma用于可能是平台特定的编译器/预处理器指令。看起来你在这里谈论的是MSVC ++ #pragmas。您可以找到their full listthe full list for gcc

其他编译器将有完全不同的列表。

回到MSVC ++,我最喜欢的一个pragma是#pragma warning。我通常会在启用“将警告视为错误”的情况下构建代码,然后手动禁用我已审核过的某些警告,以确保不会导致问题。这允许编译器帮助我在构建期间检测更多问题。

答案 6 :(得分:3)

使用VC,我过去曾使用过这个:

#define STRINGIFY( L )       #L
#define MAKESTRING( M, L )   M(L)
#define SHOWORIGIN           __FILE__ "("MAKESTRING( STRINGIFY, __LINE__) "): "

// then, later...

#pragma message( SHOWORIGIN "we need to look at this code" )
// ...
#pragma message( SHOWORIGIN "and at this, too" )

输出:

c:\...\test.cpp(8): we need to look at this code
c:\...\test.cpp(10): and at this, too

您可以在输出窗格中双击它,IDE会将您带到正确的文件和行。

答案 7 :(得分:0)

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

用于使用winsock库的项目