23

我从来不明白#pragma once什么时候#ifndef #define #endif总是有效的需要。

我已经看到了#pragma comment与其他文件链接的用法,但是使用 IDE 设置编译器设置更容易。

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

编辑:

只是在#pragma 指令列表之后。也许我应该重新表述一下这个问题:

你用什么代码写的,#pragma你觉得有用?

.

答案一目了然:

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

  • Jason 建议使用#pragma onceor#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#pragma message为 VC 用户提供了一个绝妙的技巧来输出带有行号信息的消息。James 更进一步,允许errorwarning匹配 MSVC 的消息,并将适当地显示,例如错误列表。
  • Chris 提供#pragma region了能够在 MSVC 中使用自定义消息折叠代码。

哇,等一下,如果我想发布关于使用#pragmas 的帖子,除非必要,该怎么办?

  • 从另一个角度看克利福贴一下不要#pragma。荣誉。

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

4

8 回答 8

12

每个 pragma 都有其用途,否则它们一开始就不会存在。

如果您知道不会将代码移植到不同的编译器,那么 pragma "once" 的输入就更少了,也更整洁了。它也应该更有效,因为编译器根本不需要解析标头来确定是否包含其内容。

编辑:回答评论:假设你有一个 200kB 的头文件。使用“once”,编译器会加载一次,然后知道它在下次看到它引用时根本不需要包含头文件。使用#if,它每次都必须加载和解析整个文件,以确定所有代码都被 if 禁用,因为每次都必须评估 if。在大型代码库上,这可能会产生重大影响,尽管实际上(尤其是预编译的头文件)可能不会。

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

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

pragma "error" 和 "message" 非常方便,尤其是在条件编译块中(例如,“错误:'Release for ePhone' build is unimplemented”,message: “extra debugging and profiling code is enabled in this build”)

pragma "warning"(尤其是 push 和 pop)对于暂时禁用恼人的警告非常有用,尤其是在包含编写不佳的第三方标头(充满警告)时 - 特别是如果您使用警告级别 4 构建。

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

于 2010-04-24T07:56:15.837 回答
9

正如您所提到的,我在 Visual c++ 中看到了编译指示,它告诉它在链接时链接到某个库。对于需要 winsock 库的库来说很方便。这样,您无需修改​​项目设置即可将其链接起来。例如: #pragma comment(lib,"wsock32.lib")。我喜欢这个,因为它将需要 .lib 的代码与它相关联,而且一旦你把它放在文件中,如果你在另一个项目中重用该代码,你就不会忘记它。

此外,用于打包数据结构的 pragma 通常很有用,特别是在数据成员的偏移量很重要的系统和网络编程中。前任:

#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
于 2010-04-24T07:30:20.743 回答
9

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

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

#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

这是一团糟,您很快就看不到树木的木材,并且随着您添加对更多编译器的支持,问题变得更糟(这反过来需要了解识别编译器的预定义宏。

[注意:]GCC 4.x 实际上支持 #pragma pack 以实现 MS 兼容性,因此上面的示例有些做作,但对于可能仍在使用的早期版本的 GCC 或其他编译器,情况并非如此。

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

解决您最初的问题:“您使用#pragma 编写了哪些代码,您觉得有用? ”;您应该考虑避免编译指示的有用方法吗?

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

于 2010-04-24T08:33:15.400 回答
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")

用作:

#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指令那样立即停止编译过程。

于 2010-04-25T00:26:42.737 回答
4

在 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):我们需要看看这段代码
c:\...\test.cpp(10): 在这个也是

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

于 2010-04-24T12:17:53.720 回答
4

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

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

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

于 2010-04-24T18:06:55.060 回答
3

#pragma根据定义,适用于可能特定于平台的编译器/预处理器指令。看起来您在这里谈论的是 MSVC++ #pragmas。您可以找到他们的完整列表,或gcc 的完整列表

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

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

于 2010-04-24T06:57:41.007 回答
0
#pragma comment(lib, "WS2_32.lib")

对于使用 winsock 库的项目

于 2010-04-24T12:57:31.077 回答