Oracle Solaris Studio 12.2:C++ 用户指南

附录 B Pragma

本附录介绍了 C++ 编译器 pragma。pragma 是一个编译器指令,使用它可以向编译器提供其他信息。该信息可以更改您所控制的编译详细信息。例如,pack pragma 会影响结构内的数据布局。编译器 pragma 也称为指令

预处理程序关键字 pragma 是 C++ 标准的一部分,但每个编译器中,pragma 的形式、内容和含义都是不相同。C++ 标准不定义任何 pragma。


注 –

依赖于 pragma 的代码是不可移植的。


B.1 Pragma 形式

C++ 编译器 pragma 的各种形式如下所示:


#pragma keyword
#pragma keyword ( a [ , a ] ...) [ , keyword ( a [ , a ] ...) ] ,...
#pragma sun keyword

变量 keyword 指特定指令;a 表示参数。

B.1.1 将函数作为 pragma 参数进行重载

本附录中列出了几个将函数名称作为参数的 pragma。如果重载该函数,则 pragma 使用其前面的函数声明作为其参数。请看以下示例:


int bar(int);
int foo(int);
int foo(double);
#pragma does_not_read_global_data(foo, bar)

在此示例中,foofoo(double),即 pragma 紧前面的 foo 声明;而 barbar(int),即唯一声明的 bar。现在,请看以下示例,在此示例中再次重载了 foo


int foo(int);
int foo(double);
int bar(int);
#pragma does_not_read_global_data(foo, bar)

在此示例中,barbar(int),即唯一声明的 bar。但 pragma 并不知道要使用哪个版本的 foo。要更正此问题,必须将 pragma 放在希望 pragma 使用的 foo 定义的紧后面。

以下 pragma 使用本节中介绍的选择方法:

B.2 Pragma 参考

本节描述 C++ 编译器可识别的 pragma 关键字。

B.2.1 #pragma align


#pragma align integer(variable [,variable...])

使用 align 使所列变量与 integer 字节内存对齐,并覆盖缺省设置。请遵循以下限制:


#pragma align 64 (aninteger, astring, astruct)
int aninteger;
static char astring[256];
struct S {int a; char *b;} astruct;

#pragma align 在名称空间内部使用时,必须使用损坏名称。例如,以下代码中的 #pragma align 语句就是无效的。要更正此问题,应将 #pragma align 语句中的 abc 替换为其损坏名称。


namespace foo {
    #pragma align 8 (a, b, c)
    static char a;
    static char b;
    static char c;
}

B.2.2 #pragma does_not_read_global_data


#pragma does_not_read_global_data(funcname [, funcname])

此 pragma 断言,指定的例程不直接或间接读取全局数据。允许对调用这些例程的代码进行更好的优化。具体来讲,赋值语句或存储可以围绕这样的调用移动。

指定函数的原型被声明后,该 pragma 才可用。如果全局访问的断言不为真,那么程序的行为就是未定义的。

有关 pragma 如何将重载的函数名视为参数的更加详细的说明,请参见B.1.1 将函数作为 pragma 参数进行重载

B.2.3 #pragma does_not_return


#pragma does_not_return(funcname [, funcname])

pragma 向编译器断言,将不会返回对指定例程的调用。这样编译器可以执行与假定一致的优化。例如,寄存器生命周期在调用点终止,这样可以进行进一步的优化。

如果指定的函数不返回,程序的行为就是未定义的。

指定函数的原型被声明后,该 pragma 才是可用的,如以下示例所示:


extern void exit(int);
#pragma does_not_return(exit)

extern void __assert(int);
#pragma does_not_return(__assert)

有关 pragma 如何将重载的函数名视为参数的更加详细的说明,请参见B.1.1 将函数作为 pragma 参数进行重载

B.2.4 #pragma does_not_write_global_data


#pragma does_not_write_global_data(funcname [, funcname])

此 pragma 断言,指定的一组例程不会直接或间接写入全局数据。允许对调用这些例程的代码进行更好的优化。具体来讲,赋值语句或存储可以围绕这样的调用移动。

指定函数的原型被声明后,该 pragma 才可用。如果全局访问的断言不为真,那么程序的行为就是未定义的。

有关 pragma 如何将重载的函数名视为参数的更加详细的说明,请参见B.1.1 将函数作为 pragma 参数进行重载

B.2.5 #pragma dumpmacro s


#pragma dumpmacros (value[,value...])

要查看宏在程序中如何工作时,请使用该 pragma。该 pragma 提供了诸如宏定义、取消定义和用法实例的信息,并按宏的处理顺序将输出输出到标准错误 (stderr)。dumpmacros pragma 达到文件结尾或遇到 #pragma end_dumpmacro 之前一直有效。请参见B.2.6 #pragma end_dumpmacrosvalue 可以是下列参数:

值  

含义  

defs 

输出所有宏定义 

undefs 

输出所有取消定义的宏 

use 

输出关于使用的宏的信息 

loc 

另外输出 defsundefsuse 的位置(路径名和行号)

conds 

输出在条件指令中使用的宏的信息 

sys 

输出系统头文件中所有宏的定义、取消定义和使用的信息 


注 –

子选项 loccondssysdefsundefsuse 选项的限定符。使用 loccondssys 本身并不会生成任何结果。例如,#pragma dumpmacros=loc,conds,sys 不会生成什么结果。


dumpmacros pragma 与命令行选项作用相同,但 pragma 会覆盖命令行选项。请参见A.2.122 -xdumpmacros[= value[,value...]]

dumpmacros pragma 并不嵌套,因此以下代码行中,处理 #pragma end_dumpmacros 时,将停止输出宏信息:


#pragma dumpmacros (defs, undefs)
#pragma dumpmacros (defs, undefs)
...
#pragma end_dumpmacros

dumpmacros pragma 的作用是累积的。以下代码行


#pragma dumpmacros(defs, undefs)
#pragma dumpmacros(loc)

具有和以下行相同的效果


#pragma dumpmacros(defs, undefs, loc)

如果使用选项 #pragma dumpmacros=use,no%loc,则使用的每个宏的名称仅输出一次。如果使用选项 #pragma dumpmacros=use,loc,则每次使用宏时都输出位置和宏名称。

B.2.6 #pragma end_dumpmacros


#pragma end_dumpmacros

此 pragma 标记 dumpmacros pragma 的结尾,并停止输出有关宏的信息。如果没有在 dumpmacros pragma 后面使用 end_dumpmacros pragma,dumpmacros pragma 会在文件结尾一直生成输出。

B.2.7 #pragma error_messages

#pragma error_messages (on|off| default, tag… tag)

错误消息 pragma 提供源程序内部对编译器发出的消息的控制。Pragma 只对警告消息有效。 -w 命令行选项可通过抑制所有警告消息来覆盖此 pragma。

B.2.8 #pragma fini


#pragma fini (identifier[,identifier...])

可使用 finiidentifier 标记为完成函数。此类函数应为 void 类型,不接受任何参数,当程序在程序控制下终止或从内存删除包含的共享对象时调用它们。与初始化函数一样,完成函数按链接编辑器处理的顺序执行。

在源文件中,#pragma fini 中指定的函数在该文件中的静态析构函数后面执行。在 pragma 中使用标识符之前,请先声明这些标识符。

此类函数每出现在 #pragma fini 指令中一次,就会被调用一次。

B.2.9 #pragma hdrstop

可将 hdrstop pragma 嵌入源文件的头文件中以标识源文件活前缀的结尾。例如,考虑以下文件:


example% cat a.cc
#include "a.h"
#include "b.h"
#include "c.h"
#include <stdio.h>
#include "d.h"
.
.
.
example% cat b.cc
#include "a.h"
#include "b.h"
#include "c.h"

源文件活前缀以 c.h 结束,因此可在每个文件中的 c.h 后面插入 #pragma hdrstop

#pragma hdrstop 只能位于用 CC 命令指定的源文件活前缀的结尾处。不要在任何 include 文件中指定 #pragma hdrstop

请参见A.2.162 -xpch=vA.2.163 -xpchstop=file

B.2.10 #pragma ident


#pragma ident string

可使用 ident string 放在可执行文件的 .comment 部分中。

B.2.11 #pragma init


#pragma init(identifier[,identifier...])

可使用 init identifier 标记为初始化函数。此类函数应为 void 类型,不接受任何参数,在开始执行时构造程序的内存映像的情况下调用。执行将共享对象送入内存的操作时(程序启动时,或执行某些动态装入操作时,例如 dlopen()),执行共享对象中的初始化函数。调用到初始化函数的唯一顺序就是链接编辑器静态和动态处理该函数的顺序。

在源文件中,#pragma init 中指定的函数在该文件中的静态析构函数后面执行。在 pragma 中使用标识符之前,请先声明这些标识符。

此类函数每出现在 #pragma init 指令中一次,就会被调用一次。

B.2.12 #pragma must_have_frame


#pragma must_have_frame(funcname [,funcname])

该 pragma 要求总是编译指定的一组函数来获得完整的栈帧(如 System V ABI 中所定义)。必须在使用该 pragma 列出函数之前,声明该函数的原型。


extern void foo(int);
extern void bar(int);
#pragma must_have_frame(foo, bar)

只有在声明了指定函数的原型后,才允许使用该 pragma。该 pragma 必须位于函数结尾之前。


void foo(int) {
  .
  #pragma must_have_frame(foo)
  .
  return;
  }

请参见B.1.1 将函数作为 pragma 参数进行重载

B.2.13 #pragma no_side_effect


#pragma no_side_effect(name[,name...])

可使用 no_side_effect 指示函数不更改任何持久状态。Pragma 声明了命名的函数不具有任何副作用。这意味着函数将返回仅依赖于传递参数的结果。此外,函数和后面的函数调用:

编译器执行优化时可以使用该信息。

如果函数具有副作用,执行调用该函数的程序的结果是未定义的。

name 参数指定当前转换单元中函数的名称。Pragma 必须与函数在相同的作用域,并且必须在函数声明之后出现。pragma 必须在函数定义之前。

有关 pragma 如何将重载的函数名视为参数的更加详细的说明,请参见B.1.1 将函数作为 pragma 参数进行重载

B.2.14 #pragma opt


#pragma opt level (funcname[, funcname])

funcname 指定当前转换单元中定义的函数的名称。level 值指定用于所指定函数的优化级别。可以指定优化级别 0、1、2、3、4、5。可以通过将 level 设置为 0 来禁用优化。必须在该 pragma 之前使用原型或空参数列表声明函数。pragma 则必须对要优化的函数进行定义。

pragma 中所列任何函数的优化级别都降为 -xmaxopt 值。-xmaxopt=off 时,忽略 pragma。

有关 pragma 如何将重载的函数名视为参数的更加详细的说明,请参见B.1.1 将函数作为 pragma 参数进行重载

B.2.15 #pragma pack( n)


#pragma pack([n])

可使用 pack 影响对结构成员的封装。

如果使用了该项,n 必须为 0 或 2 的幂。0 以外的值指示编译器针对数据类型使用 n 字节对齐和平台的自然对齐中的较小者。例如,以下指令使在指令后面(以及后续的 pack 指令前面)定义的所有结构成员对齐时依据的字节边界不严于 2 字节边界,即使正常对齐是 4 或 8 字节边界也是如此。


#pragma pack(2)

n 为 0 或省略该项时,成员对齐还原为自然对齐值。

如果 n 值等于或大于平台上最严格的对齐时,则采用自然对齐。下表显示了每个平台最严格的对齐。

表 B–1 平台上最严格的对齐

平台  

最严格的对齐  

x86 

SPARC 通用、V8、V8a、V8plus、V8plusa、V8plusb 

SPARC V9、V9a、V9b 

16 

pack 指令应用于自身与下一个 pack 指令之间的所有结构定义。如果在具有不同包装的不同转换单元中定义了相同的结构,那么程序会因某种原因而失败。具体来说,不应该在包括定义预编译库接口的头文件之前使用 pack 指令。建议将 pack 指令放在要封装的结构紧前面的程序代码中,并将 #pragma pack() 放在该结构紧后面。

如果在 SPARC 平台上使用 #pragma pack 封装效果比类型的缺省对齐紧密,必须为应用程序的编译和链接指定 -misalign 选项。下表显示了整型数据类型的存储大小和缺省对齐。

表 B–2 存储大小和缺省对齐字节数

类型  

SPARC V8 

大小,对齐  

SPARC V9 

大小,对齐  

x86 

大小,对齐  

bool 

1,1 

1,1 

1,1 

char 

1,1 

1,1 

1,1 

short 

2,2 

2,2 

2,2 

wchar_t 

4,4 

4,4 

4,4 

int 

4,4 

4,4 

4,4 

long 

4,4 

8,8 

4,4 

float 

4,4 

4,4 

4,4 

double 

8,8 

8,8 

8,4 

long double 

16,8 

16,16 

12,4 

指向数据的指针 

4,4 

8,8 

4,4 

指向函数的指针 

4,4 

8,8 

4,4 

指向成员数据的指针 

4,4 

8,8 

4,4 

指向成员函数的指针 

8,4 

16,8 

8,4 

B.2.16 #pragma rarely_called


#pragms rarely_called(funcname[, funcname])

pragma 提示编译器,很少调用指定函数。这样,编译器可以在此类例程的调用点执行配置文件反馈样式的优化,而没有配置文件收集阶段的开销。因为该 pragma 只是建议,所以编译器不执行基于该 pragma 的任何优化。

只有在声明指定函数的原型之后,才能使用 #pragma rarely_called 预处理程序指令。以下是 #pragma rarely_called 示例:


extern void error (char *message);
#pragma rarely_called(error)

有关 pragma 如何将重载的函数名视为参数的更加详细的说明,请参见B.1.1 将函数作为 pragma 参数进行重载

B.2.17 #pragma returns_new_memory


#pragma returns_new_memory(name[,name...])

pragma 断言,每个指定的函数都返回新分配内存的地址,以及指针没有与任何其他指针相关的别名。该信息允许优化器更好地跟踪指针值并明确内存位置。这样可以改善调度和管线操作。

如果该断言为假,那么执行调用该函数的程序的结果是未定义的。

name 参数指定当前转换单元中函数的名称。Pragma 必须与函数在相同的作用域,并且必须在函数声明之后出现。pragma 必须在函数定义之前。

有关 pragma 如何将重载的函数名视为参数的更加详细的说明,请参见B.1.1 将函数作为 pragma 参数进行重载

B.2.18 #pragma unknown_control_flow


#pragma unknown_control_flow(name[,name...])

可使用 unknown_control_flow 指定一组违反过程调用的常规控制流属性的例程。例如,可通过任意的任何其他例程调用来访问 setjmp() 调用后面的语句。该语句通过调用 longjmp() 来访问。

因为这种例程使标准流程图分析无效,调用它们的例程不能安全地优化,所以要禁用优化器来编译这些例程。

如果函数名称被重载,那么会选择最近声明的函数。

B.2.19 #pragma weak


#pragma weak name1 [= name2]

可使用 weak 定义弱全局符号。该 pragma 主要在源文件中用于生成库。链接程序在不能解决弱符号时不会发出警告。

weak pragma 可以按以下两种形式之一来指定符号:

B.2.19.1 #pragma weak name

采用 #pragma weak name 形式时,指令使 name 成为弱符号。链接程序没有找到 name 的符号定义时,不会显示错误消息,也不会出现符号的多个弱定义的错误消息。链接程序仅执行第一个遇到的定义。

如果另一个编译单元有函数或变量的强定义,那么 name 将链接到它。如果没有 name 的强定义,那么链接程序符号的值为 0。

以下指令将 ping 定义为弱符号。链接程序找不到名为 ping 的符号的定义时,不会生成错误消息。


#pragma weak ping

#pragma weak name1 = name2

采用 #pragma weak name1 = name2 形式时,符号 name1 成为对 name2 的弱引用。如果没有在其他地方定义 name1,那么 name1 的值为 name2。如果在其他地方定义了 name1,那么链接程序使用该定义并忽略对 name2 的弱引用。以下指令指示链接程序解析对 bar(如果已在程序中某处定义)的任何引用,以及解析对 foo 的引用。


#pragma weak bar = foo

采用标识符形式时,必须在当前编译单元中声明和定义 name2。例如:


extern void bar(int) {...}
extern void _bar(int);
#pragma weak _bar=bar

使用字符串形式时,符号不需要预先声明。如果以下示例中的 _barbar 都是 extern "C",则不需要声明函数。但 bar 必须在同一对象中定义。


extern "C" void bar(int) {...}
#pragma weak "_bar" = "bar"

重载函数

使用标识符形式时,必须在 pragma 位置的作用域中正好有一个具有指定名称的函数。尝试将标识符形式 #pragma weak 用于重载函数会出现错误。例如:


int bar(int);
float bar(float);
#pragma weak bar        // error, ambiguous function name

要避免错误,请使用字符串形式,如以下示例所示。


int bar(int);
float bar(float);
#pragma weak "__1cDbar6Fi_i_" // make float bar(int) weak

有关更多信息,请参见 Solaris《链接程序和库指南》。