Namespaces
Variants

Implementation defined behavior control

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

实现定义行为由 #pragma 指令控制。

目录

语法

#pragma 编译指示参数 (1)
_Pragma( 字符串字面量 ) (2) (自 C++11 起)
1) 以实现定义的方式表现。
2) 移除 字符串字面量 L 前缀(如果存在)、外层引号及首尾空白符,将每个 \ " 替换为 " ,将每个 \\ 替换为 \ ,随后对结果进行词法分析(如 翻译阶段 3 所述),最终将结果视为 (1) #pragma 的输入进行处理。

说明

Pragma 指令用于控制编译器与具体实现相关的行为,例如禁用编译器警告或更改对齐要求。任何无法识别的 pragma 都会被忽略。

非标准杂注

ISO C++ 语言标准并不要求编译器支持任何 pragma 指令。然而,多种实现确实支持若干非标准 pragma:

#pragma STDC

ISO C语言标准要求C编译器必须支持以下三种编译指示,部分C++编译器厂商在其C++前端中不同程度地支持这些指示:

#pragma STDC FENV_ACCESS arg (1)
#pragma STDC FP_CONTRACT arg (2)
#pragma STDC CX_LIMITED_RANGE arg (3)

其中 arg 可以是 ON OFF DEFAULT

1) 若设置为 ON ,将告知编译器程序将访问或修改 浮点环境 ,这意味着禁止可能破坏标志测试和模式更改的优化(例如全局公共子表达式消除、代码移动和常量折叠)。默认值为实现定义,通常为 OFF
2) 允许浮点表达式的 紧缩 ,即省略舍入误差和浮点异常的优化,这些误差和异常在表达式严格按照书写方式精确求值时会被观察到。例如,允许使用单条融合乘加CPU指令实现 ( x * y ) + z 。默认值为实现定义,通常为 ON
3) 告知编译器复数乘法、除法和绝对值运算可采用简化数学公式 (x+iy)×(u+iv) = (xu-yv)+i(yu+xv) (x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u 2
+v 2
)
|x+iy| = x 2
+y 2
,即使可能存在中间结果溢出风险。换言之,程序员需确保传递给这些函数的数值范围是受限制的。默认值为 OFF

如果上述三个 pragma 出现在任何外部声明之外,或位于复合语句内所有显式声明和语句之前以外的任何上下文中,则程序的行为是未定义的。

注意:不支持这些编译指示的编译器可能提供等效的编译时选项,例如 gcc 的 -fcx-limited-range -ffp-contract

#pragma once

#pragma once 是一个非标准预处理指令,受到 绝大多数现代编译器 的支持。当它出现在头文件中时,表示该文件即使(直接或间接地)在同一源文件中被多次包含,也仅会被解析一次。

防止同一头文件被多次包含的标准方法是使用 包含保护

#ifndef LIBRARY_FILENAME_H
#define LIBRARY_FILENAME_H
// 头文件内容
#endif /* LIBRARY_FILENAME_H */

这样,除了翻译单元中首次包含该头文件外,所有后续包含都会被排除在编译之外。所有现代编译器都会记录头文件使用包含保护的情况,只要保护宏仍处于定义状态,再次遇到该文件时就不会重新解析(参见例如 gcc )。

使用 #pragma once 时,相同的头文件将显示为

#pragma once
// 头文件内容

与头文件保护符不同, #pragma once 能有效避免在多个文件中错误使用相同宏名的情况。然而,由于该指令基于文件系统层面的标识来排除文件,若头文件在项目中的多个位置存在,则无法防止重复包含同一头文件的问题。

#pragma pack

该系列杂注控制后续定义的类和联合成员的最大对齐方式。

#pragma pack( arg ) (1)
#pragma pack() (2)
#pragma pack(push) (3)
#pragma pack(push, arg ) (4)
#pragma pack(pop) (5)

其中 arg 是2的小次幂,用于指定以字节为单位的新对齐方式。

1) 将当前对齐方式设置为值 arg
2) 将当前对齐方式设置为默认值(由命令行选项指定)。
3) 将当前对齐方式的值压入内部堆栈。
4) 将当前对齐值压入内部堆栈,然后将当前对齐设置为 arg 参数的值。
5) 从内部栈弹出顶部条目,随后将当前对齐方式设置为该值。

#pragma pack 可能会降低类的对齐要求,但无法使类产生过度对齐。

另请参阅针对 GCC MSVC 的具体实现细节。

参考文献

  • C++23 标准 (ISO/IEC 14882:2024):
  • 15.9 预处理指令 [cpp.pragma]
  • C++20 标准 (ISO/IEC 14882:2020):
  • 15.9 预处理指令 [cpp.pragma]
  • C++17 标准 (ISO/IEC 14882:2017):
  • 19.6 Pragma 指令 [cpp.pragma]
  • C++14 标准 (ISO/IEC 14882:2014):
  • 16.6 Pragma 指令 [cpp.pragma]
  • C++11 标准 (ISO/IEC 14882:2011):
  • 16.6 Pragma 指令 [cpp.pragma]
  • C++98 标准 (ISO/IEC 14882:1998):
  • 16.6 编译指示指令 [cpp.pragma]

参见

C 文档 关于 实现定义行为控制

外部链接

1. Visual Studio 中的 C++ pragma
2. GCC 接受的 Pragma
3. IBM AIX XL C 16.1 中的 独立 pragma 说明 标准 pragma
4. Sun Studio 11 C++ 用户指南中的 附录 B. Pragma
5. Intel C++ 编译器 pragma
6. HP aCC A.06.25 的 版本说明(包含 pragma)