预处理命令

ANSI C标准规定可以在C源程序中加入一些“预处理命令”(Preprocessor directives),以改进程序设计环境,提高编程效率。这些预处理命令是由ANSI C统一规定的,但是它不是C语言本身的组成部分,不能直接对它们进行编译(因为编译程序不能识别它们)。必须在对程序进行通常的编译(包括词法和语法分析、代码生成、优化等)之前,先对程序中的这些特殊的命令进行“预处理”,即根据预处理命令对程序作相应的处理(例如,若程序中用#define命令定义了一个符号常量A,则在预处理时将程序中所有的A都置换为制定的字符串。若程序中用#include命令包含了一个文件"stdio.h",则在预处理时将stdio.h文件中的实际内容代替该命令)。
经过预处理后的程序不再包括该预处理命令了,最后再由编译程序对预处理后的源程序进行通常的编译处理,得到可提供的目标代码。现在使用的许多C编译系统都包括了预处理、编译和连接等部分,在进行编译时一气呵成。因此不少人误认为预处理命令是C语言的一部分,甚至认为它们是C语句,这是不对的。必须正确区别预处理命令和C语句,区别预处理和编译,才能正确使用预处理命令。C语言与其他高级语言的一个重要区别是可以使用预处理命令和具有预处理的功能。
C提供的预处理功能主要有宏定义文件包含条件编译,这三种功能分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#”开头。

宏定义

宏定义按照是否携带参数分为两类。

不带参数的宏定义

用一个指定的标识符(即名字)来代表一个字符串,一般形式为 #define 标识符 字符串。

#define PI "3.1415926"

它的作用是在本程序文件中用指定的标识符PI来代替"3.1415926"这个字符串,在编译预处理时。将程序中在该命令以后出现的所有PI都用"3.1415926"代替。这种方法使用户能以一个简单的名字代替一个长的字符串,因此把这个标识符成为“宏名”,在预编译时将宏名替换成字符串的过程称为“宏展开”。#define 时宏定义命令。
说明:
1、宏名一般习惯用大写字母表示,以便与变量名相区别,但并非必须。
2、使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量,也可以提高程序的通用性。
3、宏定义只是作简单的置换,预编译时不做任何语法检查,如果有语法错误,只有在编译已被宏展开后的源程序时才会发现并报错。
4、宏定义不是C语句,不需要在行末添加分号。如果添加了分号,会连同分号,一起置换。例如:

#define PI 3.14;
S = PI * r * r;

经过宏展开后,该语句为

S = 3.14; * r * r;

显然出现语法错误。
5、#define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束。通常,#define命令写在文件开头,函数之前,作为文件的一部分,在此文件范围内有效。
6、可以使用#undef命令终止宏定义的作用域。例如:

#define PI 3.1415926
void main()
{
    
}
#undef
func1()
{

}
//由于#undef的租用,所以PI的作用范围到#undef终止。在函数func1中,PI将不再代表3.1415926

7、在进行宏定义时,可以引用已定义的宏名,层层置换。例如:

#define   PI   3.14
#define   R    2.0
#define   S    PI * R * R

8、宏定义时专门用于预处理命令的一个专用名词,它的定义与变量不同,只做字符替换,不分配内存空间。

带参数的宏定义

带参数的宏定义不止是进行简单的字符串替换,还要进行参数替换。其定义的一般形式为 #define 宏名(参数表) 字符串,字符串中包含在括号中所指定的参数。例如:

#define  Sum(a, b)  a + b

宏名与带参数的括号之间不应加空格,否则将空格以后的字符都作为代替字符串的一部分。

“文件包含”处理

所谓“文件包含”处理,是指一个源文件可以将另一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。C提供了#include命令来实现“文件包含”的操作。其一般形式为:

#include"文件名"  //引用的是你程序目录的相对路径中的头文件
#include<文件名>  //引用的是编译器的类库路径里面的头文件

使用#include<文件名>,系统会到存放C库函数头文件的目录中寻找要包含的文件,这成为标准方式。用#include"文件名"时,系统会先在用户当前目录中寻找要包含的文件,如果找不到,再按标准方式(即#include<>)查找。一般来说,如果用#include命令来调用库函数,则用#include<>,以节省查找时间。如果要包含的是用户自己编写的文件(此类文件一般都在用户当前目录中),一般用#include""。若文件不在当前目录中,在双引号("")内应给出文件路径(如#include"C:\lanpade\file.h")。

条件编译

一般情况下,源程序中每一行都参加编译,但是有时希望程序中一部分内容只有在满足一定条件时才进行编译,也就是在对这一部分代码,指定编译的条件,这就是“条件编译”。有时,希望在满足某条件时对某一组语句进行编译,而当条件不满足时,则编译另一组语句。
条件编译有一下几种形式:
形式一

#ifdef标识符
  程序段1
#else
  程序段2
#endif

它的作用是,若指定的标识符已经被#define命令定义过,则在程序编译阶段编译程序段1;否则编译程序段2。其中,#else部分可以没有,即

#ifdef
  程序段1
#endif

这里的“程序段”可以使语句组,也可以是命令行。这种条件编译对于提高C源程序的通用性是很有好处的。如果一个C源程序在不同计算机系统上运行,而不同的计算机又有一定的差异(例如,有的机器以16位(2字节)来存放一个整数,而有的机器则以32位存放一个整数),这样往往要对源程序做必要的修改,这就降低了程序的通用性。可以存放以下的条件编译来处理:

#ifdef COMPUTER_A
   #define INTEGER_SIZE 16
#else
   #define INTEGER_SIZE 32
#endif   

如果在这组条件编译命令之前曾出现过以下命令行:

#define COMPUTER_A 0

或将COMPUTER_A定义为任何字符串,甚至是

#define COMPUTER_A

即只要COMPUTER_A已经被定义过,则在程序编译时就编译下面的命令行:

#define INTEGER_SIZE 16

否则,就编译下面的命令行:

#define INTEGER_SIZE 32

则预编译后程序中的INTEGER_SIZE都用16代替,否则都用32代替。
这样,源程序可以不必做任何修改就可以用于不同类型的计算机系统。
形式二

#ifndef 标识符
  程序段1
#else
  程序段2
#endif

只是第一行与第一种形式不同:将“ifdef”改为“ifndef”。它的作用是,若标识符未被定义过则编译程序段1;否则编译程序段2.这种形式与第一种形式的作用相反。
以上两种形式的用法差不多,根据需要任选一种,视情况而定。
形式三

#if 表达式
  程序段1
#else
  程序段2
#endif

它的作用是当指定的表达式值为真(非零)时,就编译程序段1;否则就编译程序段2。可以事先给定条件,使程序在不同的条件下执行不同的功能。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 目录 一.预处理的工作方式... 3 1.1.预处理的功能... 3 1.2预处理的工作方式... 3 二.预处理...
    朱森阅读 1,354评论 0 2
  • 1.定义 预处理命令是c++统一规定的,但是它不是c++语言本身的组成部分,不能直接对他们进行编译 2.预处理的分...
    写代码的向日葵阅读 1,578评论 0 1
  • C语言的执行流程 c语言执行分为三步编译:编译成目标代码(.obj)链接:将目标代码与C函数库连接合并,形成最终的...
    耳_总阅读 419评论 0 0
  • 预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间...
    Elena_li阅读 601评论 0 0
  • C中的预编译宏定义 2009-02-10 作者: infobillows 来源:网络 在将一个C源程序转换为可执行...
    白水灬煮一切阅读 1,581评论 0 5