#pragma 完全介绍

Pragma是什么?
翻译:Rogers后附英文原文。


(译者注:
一句话,pragma就是为了让编译器编译出的C或C++程序与机器硬件和操作系统保持完全兼容而定义的宏扩展,
#pragma是和特定编译器相关的。)
两部分:
1.Pragma说明;
2.Pragma的语法。

一、Pragma说明(Directives)

C和C++程序的每次执行都支持其所在的主机或操作系统所具有的一些独特的特点。
一些程序,例如,需要精确控制数据存放的内存区域或控制某个函数接收的参数。
#pragma指示为编译器提供了一种在不同机器和操作系统上编译以保持C和C++完全兼容的方法。?
Pragmas是由机器和相关的操作系统定义的,通常对每个编译器来说是不同的。


二、语法(Syntax)

#pragma token-string(特征字符串)

特征字符串是一连串的字符,就是要给一个特定编译器提供说明和编译意见。

符号(#)必须是pragma所在那一行的第一个非空格字符;
#号和pragma之间可以有任意个空格符。
在#pragma之后,是可以被编译器解析的预处理特征字符。
一般认为,#pragma属于宏扩展。
如果编译器发现不认识的pragma,会提出警告,但继续编译下去。

Pragmas可以用在条件声明上,提供最新的功能性的预处理程序,或者提供给编译器定义执行的信息。
C和C++编译器认可如下pragmas:
alloc_text
comment //注释
init_seg1
optimize //最优化
auto_inline
component //组成部件
inline_depth
pack //包
bss_seg
data_seg
inline_recursion //内嵌递归
pointers_to_members1
check_stack
function
intrinsic //内在的
setlocale
code_seg
hdrstop
message
vtordisp1
const_seg
include_alias
once #pragma once的意思是要求编译器在编译过程中只将包含此命令文件编译(打开)一次,从而避免重复包含此文件。

warning


这是MSDN的一篇文章,原作者曾经想使用
#pragma pack(1) // 用GCC在MIPS平台上将结构体成员结合到一块连续的内存块,但是没有做到。

在linux环境下使用intel-based GCC,#pragma pack(1)可以工作。

建议参考具体编译器的文档,在里面应该有pragma的说明。

---------------------------------------原文-------------------------------------------

Pragma Directives
Each implementation of C and C++ supports some features unique to its host machine or operating system.

Some programs, for instance, need to exercise precise control over the memory areas where data is placed or

to control the way certain functions receive parameters.
The #pragma directives offer a way for each compiler
to offer machine- and operating-system-specific features
while retaining overall compatibility with the C and C++
languages. Pragmas are machine- or operating-system-specific by definition,
and are usually different for every compiler.

Syntax

#pragma token-string

The token-string is a series of characters that gives a specific compiler instruction and arguments,
if any.

The number sign (#) must be the first non-white-space character on the line containing the pragma;

white-space characters can separate the number sign and the word pragma.
Following #pragma, write any text that the translator can parse as preprocessing tokens.
The argument to #pragma is subject to macro expansion.

If the compiler finds a pragma it does not recognize, it issues a warning, but compilation continues.

Pragmas can be used in conditional statements, to provide new preprocessor functionality,
or to provide implementation-defined information to the compiler.
The C and C++ compilers recognize the following pragmas:

alloc_text comment init_seg1 optimize
auto_inline component inline_depth pack
bss_seg data_seg inline_recursion pointers_to_members1
check_stack function intrinsic setlocale
code_seg hdrstop message vtordisp1
const_seg include_alias once warning


This is an article from MSDN, I ever wanted to use
#pragma pack(1) //which can combine structure members to one continuous memory block
on MIPS platform using GCC, but it doesn't work.


See the compiler's document, it should be illustrated there.

Under linux env using intel-based GCC, it works.

解析#pragma指令
在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C ++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
其格式一般为: #Pragma Para
其中Para 为参数,下面来看一些常用的参数。

(1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗
口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:
#Pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_
X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了

(2)另一个使用得比较多的pragma参数是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。

(3)#pragma once (比较常用)
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。

(4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。

(5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体
外观的定义。

(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等价于:
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
#pragma warning(once:4385) // 4385号警告信息仅报告一次
#pragma warning(error:164) // 把164号警告信息作为一个错误。
同时这个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1---4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
常用的lib关键字,可以帮我们连入一个库文件。

(8)#pragma pack()
我们知道在VC中,对于想结构体Struct这样的类型,VC采用8字节对齐的方式,如果我们不想使用8字节对齐(在网络变成中经常需要这样),我们可以在结构体前面加上
#pragma pack(1)
struct
{
......
}
#pragma pack( )

以下是另一个转载:

在vc6的时代头文件一般使用ifndef define endif
在vc7的时代头文件一般成了pragma once
不知道有没有人深究其中的意义
为什么有这样的代码,是为了头文件不被重复引用,那样编译器抱错的,这两种方法都是同样的目的,有没有区别呢?
还是举例来说明,可能有好几个库,每个库内部可能都有public.h这个文件,如果使用
ifndef public_h
define public_h
...
endif
那么当一个文件同时引用两个这样的库时,后一个库里的文件就不被编译了,而pragma once可以保证文件只被编译一次
看起来pragma once比ifndef define endif要好,那么ifndef define endif
的 地方都pragma once好了。今天碰到了又一个例子,比如你有一个zlib.h在几个库都用到,而为了方便,把zlib每个目录下copy了一分,因为这个文件不会作修改,已经很完整了,这个时候如果使用pragma once,就会重复定义,看来ifndef define endif还是又派上用场的地方。
所以对于公有或者接口的文件,使用ifndef define endif,对于内部的文件使用pragma once.

#pragma once 与 #ifndef #define #endif 的区别

对于#pragma once,根据MSDN解说,能够防止一个文件被多次包含。与#ifndef #define #endif形式的文件保护相比,前者是平台相关的,可移植性比较差,但是它效率更高,因为它不需要去打开包含的文件,就可以判断这个文件有没有被包含。当然这个工作是系统帮我们完成的。
后者的优点在于它是语言相关的特性,所以可移植性好。但是在包含一个文件的时候,只有打开这个文件,根据文件的保护宏是否已经被定义来判断此文件是否已经被包含过。效率相对较低。当然在#i nclude的时候,程序员也可以自己判断所要包含的文件的保护宏是否已经被定义,来决定是否要包含这个文件。类似下面的代码:
#ifndef FILE_H_
#include "file.h"
#endif
这样作可以得到较高的效率,而且保证可移植性。但是文件之间的依赖性较高,如果一个文件的保护宏改变的话,所有使用如上形式包含这个文件的文件都要修改。有悖于模块化的思想。

<think>好的,用户现在要了解Oracle的PRAGMA AUTONOMOUS_TRANSACTION。我需要先回顾一下这个知识点的基本内容,确保自己理解正确。 首先,PRAGMA AUTONOMOUS_TRANSACTION是Oracle中用来声明自治事务的编译指令。自治事务的主要特点是独立于主事务,可以单独提交或回滚,不影响主事务的状态。这在某些特定场景下非常有用,比如需要在触发器中执行DML操作并提交,而不影响主事务的提交或回滚。 用户可能是一位开发人员或者DBA,他们在实际开发中遇到了需要独立事务的情况,比如日志记录。这时候使用自治事务可以避免主事务的锁竞争或者保持日志的独立性。需要确定用户的使用场景,才能给出更具体的建议。 接下来要组织回答的结构,先解释基本概念,然后说明工作原理,接着是使用场景,最后给出示例代码。这样用户能逐步理解。同时要提醒注意事项,比如自治事务的隔离性、资源占用和DDL限制,避免用户误用。 可能用户在使用触发器或存储过程时遇到了事务处理的问题,比如在触发器中无法提交导致主事务被影响,这时候自治事务就派上用场。需要确保用户明白自治事务和主事务的独立性,提交自治事务不会影响主事务的状态。 另外,要检查示例代码是否正确,特别是PRAGMA的位置,必须是在存储过程、函数或触发器的声明部分。比如在存储过程中使用AUTONOMOUS_TRANSACTION,确保代码示例准确无误。 用户可能的深层需求是希望了解如何在Oracle中处理需要独立提交的事务,避免事务之间的相互影响。因此,除了基本介绍,还需要强调适用场景和注意事项,帮助他们正确应用。 最后,总结自治事务的优缺点,帮助用户权衡是否在特定情况下使用。比如虽然自治事务提供了独立性,但过度使用可能增加复杂性和资源消耗,需要合理评估。</think>在 Oracle 数据库中,`PRAGMA AUTONOMOUS_TRANSACTION` 是一个编译指令(编译指示),用于声明一个**自治事务**(Autonomous Transaction)。自治事务是一个独立于主事务的子事务,允许在代码块(如存储过程、函数、触发器等)中执行独立的数据库操作(提交或回滚),而不会影响主事务的状态。 --- ### **核心概念** 1. **自治事务的特性**: - **独立性**:自治事务与主事务完全隔离。即使主事务回滚,自治事务的提交操作仍然有效,反之亦然。 - **提交/回滚自主性**:自治事务必须显式提交(`COMMIT`)或回滚(`ROLLBACK`),否则会引发错误。 - **用途**:常用于日志记录、审计、错误处理等场景,需保证某些操作不受主事务结果的影响。 2. **语法**: 在声明存储过程、函数、触发器或匿名块时,添加以下指令: ```sql PRAGMA AUTONOMOUS_TRANSACTION; ``` --- ### **工作原理** - **主事务**和**自治事务**各自维护独立的事务上下文。 - 自治事务的提交仅影响自身操作,主事务的提交或回滚对自治事务无影响。 ![自治事务示意图](https://img-blog.csdnimg.cn/20201202145717657.png) --- ### **使用场景** 1. **触发器中的独立操作** 例如:在触发器中记录日志,即使主事务回滚,日志仍需保留。 ```sql CREATE OR REPLACE TRIGGER log_audit_trigger AFTER INSERT ON employees FOR EACH ROW DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO audit_log (action, timestamp) VALUES ('New employee added', SYSTIMESTAMP); COMMIT; -- 必须显式提交 END; ``` 2. **存储过程/函数中的独立事务** 例如:在存储过程中记录错误信息,不受主事务结果影响。 ```sql CREATE OR REPLACE PROCEDURE log_error (p_message VARCHAR2) AS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO error_log (message, log_time) VALUES (p_message, SYSTIMESTAMP); COMMIT; -- 显式提交 END; ``` 3. **递归调用中的事务控制** 避免主事务与嵌套事务之间的锁冲突。 --- ### **注意事项** 1. **显式提交/回滚** 自治事务必须包含 `COMMIT` 或 `ROLLBACK`,否则会报错:`ORA-06519: 检测到活动自治事务,已回退`。 2. **资源隔离性** 自治事务无法直接访问主事务的未提交数据(如未提交的 DML 变更),因为它们在独立的事务上下文中运行。 3. **DDL 操作限制** 在自治事务中执行 DDL(如 `CREATE TABLE`)会隐式提交,可能影响事务逻辑。 4. **性能影响** 频繁创建自治事务会增加数据库资源开销,需合理评估使用场景。 --- ### **示例代码** ```sql -- 自治事务的存储过程示例 CREATE OR REPLACE PROCEDURE autonomous_demo AS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO test_table (id, name) VALUES (1, 'Autonomous Data'); COMMIT; -- 必须显式提交 END; / -- 主事务中调用自治事务 BEGIN INSERT INTO test_table (id, name) VALUES (2, 'Main Data'); autonomous_demo(); -- 自治事务提交,主事务未提交 ROLLBACK; -- 主事务回滚,但自治事务的数据仍保留 END; ``` --- ### **总结** `PRAGMA AUTONOMOUS_TRANSACTION` 是 Oracle 中实现事务隔离的强有力工具,适用于需独立提交的子操作场景。合理使用可提升灵活性和可靠性,但需注意事务边界和资源管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值