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

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


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

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

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

  • does_not_read_global_data

  • does_not_return

  • does_not_write_global_data

  • no_side_effect

  • opt

  • rarely_called

  • returns_new_memory

B.2 Pragma 参考

本节介绍了 C++ 编译器可识别的 pragma 关键字。

  • align

    使参数变量内存对齐到指定数目的字节,并覆盖缺省。

  • does_not_read_global_data

    断言函数的指定列表不直接或间接读取全局数据。

  • does_not_return

    向编译器断言,调用的指定函数不返回。

  • does_not_write_global_data

    断言函数的指定列表不直接或间接写入全局数据。

  • dump_macros

    提供关于在代码中使用宏的信息。

  • end_dumpmacros

    标记 dump_macros pragma 的结尾。

  • fini

    将指定函数标记为完成函数。

  • hdrstop

    标识预编译头文件的源文件活前缀的结束。

  • ident

    将指定的字符串放在可执行文件的 .comment 部分。

  • init

    将指定函数标记为初始化函数。

  • no_side_effect

    表示函数不更改任何持续状态。

  • pack (n )

    控制结构偏移的布局。n 值是指定结构成员所需的最低对齐的数字(0、1、2、4 或 8)。

  • rarely_called

    告知编译器很少调用指定的函数。

  • returns_new_memory

    断言每个命名的函数返回新分配的内存的地址,并且该指针的别名与任何其他指针的别名不同。

  • unknown_control_flow

    指定了违反程序调用的通用控制流属性的例程列表。

  • weak

    定义弱符号绑定。

B.2.1 #pragma align


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

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

  • integer 必须是介于 1 和 128 之间的 2 的幂,有效值包括 1、2、4、8、16、32、64 和 128。

  • variable 是全局或静态变量,但不能是局部变量或类成员变量。

  • 如果指定的对齐比缺省小,就使用缺省。

  • Pragma 行必须显示在所涉及的变量的声明之前,否则该行被忽略。

  • 在 pragma 行上涉及但不在下面 pragma 行的代码中声明的任何变量都被忽略。以下示例中的变量是正确声明的。


#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 语句中的 ab 和 c 替换为其损坏名称。


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 达到文件结尾或遇到 #pragmaend_dumpmacro 之前一直有效。请参见B.2.6 #pragma end_dumpmacrosvalue 可以是下列参数:

含义

defs

打印所有宏定义

undefs

打印所有取消定义的宏

use

打印关于使用的宏的信息

loc

另外打印 defsundefs 和 use 的位置(路径名和行号)

conds

打印在条件指令中使用的宏的信息

sys

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

 

 


注 –

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


dumpmacros pragma 与命令行选项作用相同,但 pragma 会覆盖命令行选项。请参见A.2.120 -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 fini


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

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

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

B.2.8 #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.155 -xpch=vA.2.156 -xpchstop=file

B.2.9 #pragma ident


#pragma ident string

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

B.2.10 #pragma init


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

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

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

B.2.11 #pragma no_side_effect


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

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

  • 不读取或写入调用点的调用者中可视的程序状态的任何部分。

  • 不执行 I/O。

  • 不更改调用点不可视程序状态的任何部分。

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

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

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

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

B.2.12 #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.13 #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

4

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

8

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

浮点

4,4

4,4

4,4

双精度

8,8

8,8

8,4

长双精度

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.14 #pragma rarely_called


#pragms rarely_called(funcname[, funcname])

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

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


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

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

B.2.15 #pragma returns_new_memory


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

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

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

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

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

B.2.16 #pragma unknown_control_flow


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

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

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

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

B.2.17 #pragma weak


#pragma weak name1 [= name2]

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

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

  • 字符串形式。字符串必须是 C++ 变量或函数的损坏名称。无效损坏名称引用的行为是不可预测的。后端可以或不可以产生无效损坏名称引用的错误。无论是否产生错误,使用无效损坏名称时后端的行为都是不可预测的。

  • 标识符形式。标识符必须是在编译单元中以前声明的 C++ 函数的明确标识符。标识符形式不能用于变量。前端 (ccfe) 遇到无效的标识符引用时会生成错误消息。

B.2.17.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

使用字符串形式时,符号不需要预先声明。如果以下示例中的 _bar 和 bar 都是 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`#pragma` 的使用方法如下: ```c++ #pragma 指令名 [指令参数] ``` 其中,`指令名` 表示要执行的 `#pragma` 指令的名称,可以是编译器提供的预定义指令,也可以是用户自定义的指令;而 `指令参数` 则是指令的参数,不同的指令可以接受不同的参数。 下面以 `#pragma once` 和 `#pragma pack` 为例,介绍 `#pragma` 的具体使用方法。 1. 防止头文件被重复包含 `#pragma once` 可以用于防止头文件被重复包含。例如,我们有一个头文件 `my_header.h`,内容如下: ```c++ #ifndef MY_HEADER_H #define MY_HEADER_H // 头文件内容 #endif ``` 这是常见的防止头文件被重复包含的写法。但其实还有一种更加简单的方式,就是使用 `#pragma once` 指令。只需要在头文件的开头加上一行 `#pragma once`,就可以达到同样的效果,如下所示: ```c++ #pragma once // 头文件内容 ``` 2. 控制结构体字节对齐方式 `#pragma pack` 可以控制结构体的字节对齐方式。例如,我们有一个结构体 `MyStruct`,定义如下: ```c++ struct MyStruct { char a; int b; short c; }; ``` 默认情况下,编译器会按照“自然对齐”的方式来对结构体进行字节对齐,即按照结构体成员的大小和类型自动调整对齐方式。但是,如果我们希望手动控制结构体的字节对齐方式,可以使用 `#pragma pack` 指令。例如,我们希望将结构体 `MyStruct` 的字节对齐方式设置为 1,可以这样写: ```c++ #pragma pack(1) struct MyStruct { char a; int b; short c; }; ``` 这样,编译器就会按照 1 字节的对齐方式来对结构体进行字节对齐。注意,使用 `#pragma pack` 指令可能会影响程序的性能和可移植性,应该谨慎使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值