C 语言 —— 其他预处理指令

程序员可能要为不同的工作环境准备 C 程序和 C 库包。不同的环境可能使用不同的代码类型。预处理器提供一些指令,程序员通过修改 #define 的值即可生成可移植的代码。#undef 指令取消之前的 #define 定义。#ifdef、#ifndef、#else、#endif、#if 和 #elif 指令用于条件编译,即指定什么情况下编写哪些代码。#line 指令用于重置行和文件信息,#error 指令用于给出错误消息,#pragma 指令用于向编译器发出指令。

#undef 指令

#undef 指令用于“取消”已定义的 #define 指令。也就是说,假设有如下定义:

#define LIMIT 400

然后,下面的指令将移除上面的定义:

#undef LIMIT

现在就可以把 LIMIT 重新定义为一个新值。即使原来没有定义 LIMIT,取消 LIMIT 的定义仍然有效。

如果想使用一个名称,又不确定之前是否已经用过,为安全起见,可以用 #undef 指令取消该名字的定义。
需要注意的是,这里的定义指的是由预处理器定义的,因此像 int q; 这种不是宏的变量定义对预处理器来说是未定义的。

条件编译指令

可以使用其他指令创建条件编译(conditinal compilation)。也就是说,可以使用这些指令告诉编译器根据编译时的条件执行或忽略信息(或代码)块。

#ifdef、#ifndef、#else、#endif

#ifdef MAVIS 
    #include "horse.h" // 如果已经用#define定义了 MAVIS,则执行下面的指令 
    #define STABLES 5 
#else 
    #include "cow.h" //如果没有用#define定义 MAVIS,则执行下面的指令
    #define STABLES 15 
#endif 

#ifdef 指令说明,如果预处理器已定义了后面的标识符(MAVIS),则执行 #else 或 #endif 指令之前的所有指令并编译所有 C 代码(先出现哪个指令就执行到哪里)。如果预处理器未定义 MAVIS,且有 #else 指令,则执行 #else 和 #endif 指令之间的所有代码。
#ifdef #else 很像 C 的 if else。两者的主要区别是,预处理器不识别用于标记块的花括号({}),因此它使用 #else(如果需要)和 #endif(必须存在)来标记指令块。这些指令结构可以嵌套,也可以用这些指令标记 C 语句块。
#ifndef 指令与 #ifdef 指令的用法类似,也可以和 #else、#endif 一起使用,但是它们的逻辑相反。#ifndef 指令判断后面的标识符是否是未定义的,常用于定义之前未定义的常量。
#ifndef 的作用:

  1. #ifndef 指令可以防止相同的宏被重复定义。在首次定义一个宏的头文件中用 #ifndef 指令激活定义,随后在其他头文件中的定义都被忽略。
  2. #ifndef 指令通常用于防止多次包含一个文件。也就是说,应该像下面这样设置头文件:
/* things.h 文件中*/ 
#ifndef THINGS_H_ 
#define THINGS_H_ 
	/* 省略了头文件中的其他内容*/
#endif 

假设该文件被包含了多次。当预处理器首次发现该文件被包含时,THINGS_H_是未定义的,所以定义了 THINGS_H_,并接着处理该文件的其他内容。当预处理器第2次发现该文件被包含时,THINGS_H_是已定义的,
所以预处理器跳过了该文件的其他部分。
为何要多次包含一个文件?最常见的原因是,许多被包含的文件中都包含着其他文件,所以显式包含的文件中可能包含着已经包含的其他文件。
这有什么问题?在被包含的文件中有某些项(如,一些结构类型的声明)只能在一个文件中出现一次。C 标准头文件使用 #ifndef 技巧避免重复包含。
但是,这存在一个问题:如何确保待测试的标识符没有在别处定义?通常,实现的供应商使用这些方法解决这个问题:用文件名作为标识符、使用大写字母、用下划线字符代替文件名中的点字符、用下划线字符做前缀或后缀(可能使用两条下划线)。
例如,查看 stdio.h 头文件,可以发现许多类似的代码:

#ifndef _STDIO_H 
#define _STDIO_H 
	// 省略了文件的内容 
#endif 

#if 和 #elif 指令

#if 指令很像 C 语言中的 if。#if 后面跟整型常量表达式,如果表达式为非零,则表达式为真。可以在指令中使用 C 的关系运算符和逻辑运算符:

#if SYS == 1 
#include "ibm.h" 
#endif

可以按照 if else 的形式使用 #elif(早期的实现不支持 #elif)。

#if SYS == 1 
	#include "ibmpc.h" 
#elif SYS == 2 
	#include "vax.h" 
#elif SYS == 3 
	#include "mac.h" 
#else 
	#include "general.h" 
#endif 

条件编译还有一个用途是让程序更容易移植。改变文件开头部分的几个关键的定义,即可根据不同的系统设置不同的值和包含不同的文件。

预定义宏

image.png

#line 和 #error

#line 指令重置 _ LINE _和 _ FILE _宏报告的行号和文件名。可以这样使用 #line:

#line 1000 // 把当前行号重置为1000 
#line 10 "cool.c" // 把行号重置为10,把文件名重置为cool.c 

#error 指令让预处理器发出一条错误消息,该消息包含指令中的文本。如果可能的话,编译过程应该中断。可以这样使用 #error 指令:

#if _ _STDC_VERSION_ _ != 201112L 
	#error Not C11 
#endif 
// 编译以上代码生成后,输出如下:
$ gcc newish.c 
newish.c:14:2: error: #error Not C11 
$ gcc -std=c11 newish.c 
$

#prama 指令

在现在的编译器中,可以通过命令行参数或IDE菜单修改编译器的一些设置。#pragma 把编译器指令放入源代码中。例如,在开发 C99 时,标准被称为 C9X,可以使用下面的编译指示(pragma)让编译器支持 C9X:

#pragma c9x on

在笔试中常遇到的 #param pack(4) 表示指定结构、联合和类成员的封装对齐。其实就是改变编译器的内存对齐方式。其中n的取值必须是 2 的幂次方,即 1、2、4、8、16 等,Windows 下默认是 8,Linux 下默认是 4。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值