C语言#line、#error和#pragma命令,以及_pragma运算符
本文将介绍几种预处理命令及其使用方法,其中包括 #line 命令、#error 命令和 #pragma 命令。此外,还讲述了_Pragma 运算符的相关知识。
#line 命令的下一行行号会指定为 line_number。如果该命令也包含可选字符串字面量"filename",那么该编译器会把该字符串名称作为当前源文件名。
line_number 必须是大于 0 的十进制常量。如下例所示:
包含 #line 命令的那一行代码,也可以包含其他宏。如果包含其他宏,预处理器会先展开所有宏,然后再执行 #line 命令。但要确保在宏展开后,#line 命令是正确的。
程序可以利用标准预定义宏 __LINE__ 和 __FILE__ 来访问当前的行号和文件名设置:
#line 命令通常用在将 C 源代码作为输出的程序上。通过将对应的输入文件行号放置在 #line 命令内,程序可以让 C 编译器的错误消息指向源文件中相应的行。
如果上述命令存在可选项 text,则 text 就会被包含在预处理器的错误消息中。然后,编译器会终止处理源代码,并结束执行,仿佛遇到了严重错误。text可以是任意预处理器记号序列。如果 text 中有其他宏,它们都不会被展开。最好在这里使用字符串字面量,以避免标点符号字符(如单引号)的影响。
下面的例子测试标准宏 __STDC__ 是否已经被定义。如果没有,则生成一个错误消息:
如果 #pragma 之后的第一个标记(token)是 STDC,那么该命令就是一个标准 pragma。否则,该 #pragma 命令的作用取决于实现版本。为了保障代码的可移植性,应该尽量少使用 #pragma 命令。
如果预处理器支持所指定的标记,就会执行这些标记所代表的动作,或者把该信息传递给编译器。如果预处理器不支持所指定的标记,就忽略该 #pragma 命令。
例如,最新版本的 GNU C 编译器和微软 Visual C 编译器都支持 #pragma pack(n),它使得编译器让结构成员对齐到特定的字节边界。下面的例子使用 pack(1)指示每个结构成员必须对齐到字节边界:
单字节对齐方式可以确保结构成员之间不会有间隙。pack 的参数 n 通常是 2 的幂(但幂值较小)。例如,pack(2)把结构成员对齐到偶数地址,而 pack(4)把结构成员对齐到 4 为倍数的地址。pack()没有参数,它指示对齐方式设置为实现版本的默认值。
C99 新增下面三个标准的 pragma:
_Pragma 运算符的工作方式是这样的。首先,该 string_literal 操作数会被“解字符串化”(destringized),或被转换为预处理器记号序列,该过程如下:删除字符串前后的双引号;使用"替代 \";使用 \ 替代 \\。然后,预处理器会翻译前述结果序列的记号,类似于 #pragma 命令。
下面这一行代码定义了一个名为 STR 的宏,借此可以使用 _Pragma 运算符重写 #pragma 命令:
有了上述定义,下面两行代码是等同的:
下面的例子是在宏中使用 _Pragma 运算符:
宏替换会把调用宏 ALIGNMENT(2)的过程重写为如下形式:
预处理器接着处理这行代码,实现方式与使用下面的命令一样:
定义行号
编译器会在警告消息、错误消息与调试信息中包含代码所在的行号与所在的源文件名,并提供给调试工具。你可以在源代码中利用 #line 命令改变编译器默认指定的文件名与行号信息。#line 命令的语法如下:#line line_number ["filename"]
#line 命令的下一行行号会指定为 line_number。如果该命令也包含可选字符串字面量"filename",那么该编译器会把该字符串名称作为当前源文件名。
line_number 必须是大于 0 的十进制常量。如下例所示:
#line 1200 "primary.c"
包含 #line 命令的那一行代码,也可以包含其他宏。如果包含其他宏,预处理器会先展开所有宏,然后再执行 #line 命令。但要确保在宏展开后,#line 命令是正确的。
程序可以利用标准预定义宏 __LINE__ 和 __FILE__ 来访问当前的行号和文件名设置:
printf( "This message was printed by line %d in the file %s.\n", __LINE__, __FILE__ );
#line 命令通常用在将 C 源代码作为输出的程序上。通过将对应的输入文件行号放置在 #line 命令内,程序可以让 C 编译器的错误消息指向源文件中相应的行。
生成错误消息
无论是否有实际错误,#error 命令都会让预处理器发出错误消息。它的语法如下:#error [text]
如果上述命令存在可选项 text,则 text 就会被包含在预处理器的错误消息中。然后,编译器会终止处理源代码,并结束执行,仿佛遇到了严重错误。text可以是任意预处理器记号序列。如果 text 中有其他宏,它们都不会被展开。最好在这里使用字符串字面量,以避免标点符号字符(如单引号)的影响。
下面的例子测试标准宏 __STDC__ 是否已经被定义。如果没有,则生成一个错误消息:
#ifndef __STDC__ #error "This compiler does not conform to the ANSI C standard." #endif
#pragma 命令
#pragma 命令是向编译器提供额外信息的标准方法,其格式如下:#pragma [tokens]
如果 #pragma 之后的第一个标记(token)是 STDC,那么该命令就是一个标准 pragma。否则,该 #pragma 命令的作用取决于实现版本。为了保障代码的可移植性,应该尽量少使用 #pragma 命令。
如果预处理器支持所指定的标记,就会执行这些标记所代表的动作,或者把该信息传递给编译器。如果预处理器不支持所指定的标记,就忽略该 #pragma 命令。
例如,最新版本的 GNU C 编译器和微软 Visual C 编译器都支持 #pragma pack(n),它使得编译器让结构成员对齐到特定的字节边界。下面的例子使用 pack(1)指示每个结构成员必须对齐到字节边界:
#if defined( __GNUC__ ) || defined( _MSC_VER ) #pragma pack(1) // 对齐字节,没有填充 #endif
单字节对齐方式可以确保结构成员之间不会有间隙。pack 的参数 n 通常是 2 的幂(但幂值较小)。例如,pack(2)把结构成员对齐到偶数地址,而 pack(4)把结构成员对齐到 4 为倍数的地址。pack()没有参数,它指示对齐方式设置为实现版本的默认值。
C99 新增下面三个标准的 pragma:
#pragma STDC FP_CONTRACT on_off_switch #pragma STDC FENV_ACCESS on_off_switch #pragma STDC CX_LIMITED_RANGE on_off_switch on_off_switch 的值必须是 ON、OFF 或 DEFAULT。
_Pragma 运算符
无法通过宏展开创建一个 #pragma 命令(或任何其他命令)。当碰到需要这么做的情况时,C99 新增了一个预处理运算符 _Pragma,它可以与宏配合使用。它的语法如下:_Pragma (string_literal )
_Pragma 运算符的工作方式是这样的。首先,该 string_literal 操作数会被“解字符串化”(destringized),或被转换为预处理器记号序列,该过程如下:删除字符串前后的双引号;使用"替代 \";使用 \ 替代 \\。然后,预处理器会翻译前述结果序列的记号,类似于 #pragma 命令。
下面这一行代码定义了一个名为 STR 的宏,借此可以使用 _Pragma 运算符重写 #pragma 命令:
#define STR(s) #s // 这个#是“字符串化”运算符
有了上述定义,下面两行代码是等同的:
#pragma tokens _Pragma ( STR(tokens) )
下面的例子是在宏中使用 _Pragma 运算符:
#define ALIGNMENT(n) _Pragma( STR(pack(n)) ) ALIGNMENT(2)
宏替换会把调用宏 ALIGNMENT(2)的过程重写为如下形式:
_Pragma( "pack(2)" )
预处理器接着处理这行代码,实现方式与使用下面的命令一样:
#pragma pack(2)
所有教程
- socket
- Python基础教程
- C#教程
- MySQL函数
- MySQL
- C语言入门
- C语言专题
- C语言编译器
- C语言编程实例
- GCC编译器
- 数据结构
- C语言项目案例
- C++教程
- OpenCV
- Qt教程
- Unity 3D教程
- UE4
- STL
- Redis
- Android教程
- JavaScript
- PHP
- Mybatis
- Spring Cloud
- Maven
- vi命令
- Spring Boot
- Spring MVC
- Hibernate
- Linux
- Linux命令
- Shell脚本
- Java教程
- 设计模式
- Spring
- Servlet
- Struts2
- Java Swing
- JSP教程
- CSS教程
- TensorFlow
- 区块链
- Go语言教程
- Docker
- 编程笔记
- 资源下载
- 关于我们
- 汇编语言
- 大数据
- 云计算
- VIP视频