C 预处理器和C库

编译程序之前,先由预处理器检查程序(因此称为预处理器)。根据程序中使用的预处理指令,预处理器用符号缩略语所代表的内容替换程序中的缩略语。

常量#define

每个#define(即逻辑行)由三步分组成。

  • 第一部分为指令#define本身

  • 第二部分为所选择的缩略语,这些缩略语成为宏(macro)。一些宏用来代表值,它们被成为对象宏(object-like macro)。宏的名字中必须遵循C变量命名规则。

  • 第三部分(#define行的剩余部分)称为替换列表(replacement list)或主体(body)。

预处理器在程序中发现了宏的实例后,总会用该实体代替该宏。从宏变成最终的替换文本的过程成为宏展开(macro expansion)。

C编译器在编译时对所有常量表达式(只包含常量表达式)求值。预处理器不进行计算,它只是按照指令进行文字替换操作

使用const您可以创建全局常量和局部常量/数字常量/数组常量和结构常量。宏常量可以用来指定标准数组的大小并作为const的初始化值。

#define LIMIT 20
const int LIM = 50; 
static int data1[LIMIT];    // 合法
static int data2[LIM];      // 无效
const int LIM2 = 2 * LIMIT; // 合法
const int LIM3 = 2 * LIM;   // 无效

在#define中使用参数

通过使用参数,可以常见外形和作用都与函数相似的类函数(function-like macro)。宏的参数也用圆括号括起来。

        [    宏  ] [  替换主体  ]
          [宏参数]
#define MEAN(X,Y) (((X)+(Y))/2)

EXAMPLE:

#define SQUARE(X) X*X

其中,SQURE为宏标识符,SQUARE(X)中的X为宏的参数,x*x为替换列表。

利用宏参数差un关键字符串:#运算符

在类函数宏的替换部分中,#符号用做一个预处理运算符,它可以把语言符号转化为字符串。

预处理器的粘合剂:##运算符

##运算符可以用于函数宏的替换部分。

#include文件包含

预处理器发现#include指令后,就会寻找后跟的文件名,并把这个文件的内容包含到当前文件中。被包含文件中的文本将替换代码文件中的#include指令,就像把被包含文件中的全部内容键入到源文件中的这个特定位置一样。

#incldue < stido.h >文件名放在尖括号中
#include “mystuff.H”文件名放在双引号中

在UNIX系统中,尖括号告诉预处理器在一个或多个标准系统中寻找文件。双引号告诉预处理器在当前目录(或文件名中指定的目录)中寻找文件,然后在标准位置寻找文件。

#incldue < stido.h >搜索系统目录
#include “mystuff.H”搜索当前目录
#include “/usr/biff/p.H”搜索/usr/biff目录

使用头文件

头文件内容的最常见形式包括:

  • 明显常量 例如:典型的stdio.h文件定义EOF/NULL/BUFSIZE(标准I/O缓冲区的大小)

  • 宏函数 例如:getchar()通常被定义为getc(stdin),getc()通常定义为较复杂的宏,而头文件ctype.h通常包含ctype函数的定义。

  • 函数声明 例如:头文件string.h包含字符串函数系列的函数声明。

  • 结构模板定义 标准I/O函数使用FILE结构,该结构包含文件及文件相关缓冲区的信息。头文件stdio.h中存放FILE结构的声明。

  • 类型定义

可以使用头文件来声明多个文件共享的外部变量。此时,可以在包含函数声明的源代码文件定义一个具有文件作用域/外部链接的变量。

itn status = 0; // 文件作用域,源代码文件中

// 接着,可以在与源代码文件相关联的头文件中进行引用声明:

extern int status; // 头文件中

该行代码会出现在包含了该头文件的任何文件中,并使得使用这个头文件的文件都可以使用该变量。

其他指令

预处理器提供一些指令来帮助程序员编写这样的代码:

  • 改变一些#define宏的值后,这些代码可以从一个系统移植到另一个系统。
  • #undef指令取消前面的#define定义
  • #if/#ifdef/#ifndef/#else/#elif/#endif指令可以用于选择声明情况下编译那些代码。
  • #line指令用于重置行和文件信息
  • #error指令用于给出错误消息
  • #pragma指令用于向编译器发出指令

#undef指令

#undef指令用于取消定义一个给定的#define

#define LIMIT 400   // 定义LIMIT
#undef LIMIT        // 取消LIMIT定义

取消定义后可以重新定义LIMIT,以使他有一个新的值。即使开始没有定LIMIT,取消LIMIT定义也是合法的。

如果想使用一个特定名字,但又不能确定前面是否已经使用了该名字,为了安全起见,就可以取消该名字的定义。

宏定义注意点

#define宏的作用域从文件中的定义点开始,直到用#undef指令取消宏为止,或直到文件尾为止(由二者中最先满足的那个结束宏的作用域)。还应该主语,如果用头文件引入宏,那么,#define在文件中的位置依赖于#include指令的位置。

条件编译

可以使用指令告诉编译器:根据编译时的条件接受或忽略代码块。

#ifdef #else 和 #endif指令

#ifdef MAVIS 
    #include "test.h" // 如果已定义了MAVIS,则执行这里的指令
    #define STABLES 5
#else 
    #include "test2.h" // 如果没定义MAVIS,则执行这里的指令
    #define STABLES 15
#endif

#ifef #else格式非常类似于C中的if else。主要差异为预处理器不能够识别标记代码块的花括号{},因此使用#else(如果需要)和#endif(必须存在)来标记指令块。

可以使用这种方法辅助调试程序。

#ifndef指令

#ifnedf指令可以与#else/#endif指令一起使用。#ifndef判断后面的标识符是否为未定义的。

一般的,当某个文件包含几个头文件,而且每个头文件都可能包含相同的宏时,使用#ifndef可以防止对该宏重复定义。此时,第一个头文件中的定义变成有效定义,而其他头文件的定义则被忽略。

#ifndef指令通常用于防止多次包含同一个文件,头文件可以采用类似下面的设置:

#ifndef THINGS_H_
#define THINGS_H_
#endif

为什么会多次包含同一个文件呢?最常见的原因是:许多包含文件自身包含了其他文件,因此可能显示的包含其他文件已经包含的文件。

为什么这个会成为问题? 因为头文件中的有些语句在一个文件中只能出现一次。

所以,通常采用#ifndef来解决这个问题。

#if和#elif指令

#if 指令更像常规的C中的if:#if后跟常量整数表达式。如果表达式非零为真,零为假。

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

or 

#if SYS == 1
...
#elif SYS == 2
...
#endif


or 

#if defined(VAX)

or 

#if defined(VAX)
..
#elif defined(VAX)
..
#endif

defined()是一个预处理器运算符。如果defined的参数已用#define定义过,则defined返回1。它的有点在于可以和#elif一起使用.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值