黑马程序员——零基础学习iOS开发——09 预处理指令
来源:互联网 发布:数据对比分析方法 编辑:程序博客网 时间:2024/05/22 02:13
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
一、预处理指令简介
1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译
2.为了区分预处理指令和一般的C语句,所有预处理指令都以符号"#"开头,并且结尾不用分号
3.预处理指令可以出现在程序的任何位置,它的作用范围是从它出现的位置到文件尾。习惯上我们尽可能将预处理指令写在源程序开头,这种情况下,它的作用范围就是整个源程序文件
4.C语言提供的预处理指令主要有:宏定义、文件包含、条件编译
二、预处理指令——宏定义
1.不带参数的宏定义
1)一般形式
#define 宏名字符串
比如#define ABC10
右边的字符串也可以省略,比如#define ABC
2)作用
它的作用是在编译预处理时,将源程序中所有"宏名"替换成右边的"字符串",常用来定义常量。
接下来写个程序根据圆的半径计算周长
#include <stdio.h> // 源程序中所有的宏名PI在编译预处理的时候都会被3.14所代替 #define PI 3.14 // 根据圆的半径计radius算周长 float girth(float radius) { return 2 * PI *radius; } int main () { float g = girth(2); printf("周长为:%f", g); return 0; }
在第4行定义了一个叫PI的宏,在编译预处理之后,第8行中的2 * PI *radius就会变成2 *3.14 * radius。
3)使用习惯与注意
1>宏名一般用大写字母,以便与变量名区别开来,但用小写也没有语法错误。
2>对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。
3>在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查。
<span style="font-size:12px;">#define I 100int main (){ // 在做编译预处理的时候,不管语法对不对,第5行的I都会被替换为100。不过在编译的时候就会报第4行的错。 int i[3] = I; return 0;}</span>
4)宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可以用#undef命令
#define PI 3.14#undef PI// PI这个宏在第1行到第8行之间是有效的,第8行后就无效了
5)定义一个宏时可以引用已经定义的宏名
#define R 3.0#define PI 3.14#define L 2*PI*R#define S PI*R*R
2.带参数的宏定义
1)一般形式
#define 宏名(参数列表)字符串
2)作用
在编译预处理时,将源程序中所有宏名替换成字符串,并且将字符串中的参数用宏名右边参数列表中的参数替换
#include <stdio.h>#define average(a, b) (a+b)/2int main (){ int a = average(10, 4); printf("平均值:%d", a); return 0;}
第3行中定义了一个带有2个参数的宏average,第7行其实会被替换成:int a = (10 +4)/2;,输出结果为:。是不是感觉这个宏有点像函数呢?
3.使用注意
1)宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串
#define average (a, b) (a+b)/2int main (){ int a = average(10, 4); return 0;}
2)带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数,以免导致计算结果不是我们想要的结果。
如果定义宏的时候不用小括号括住参数:
#include <stdio.h>#define D(a) 2*aint main (){ // 由于第三行字符串中a没有加括号,D(3+4)将被替换成 2*3+4,而我们想要的却是2*(3+4) int b = D(3+4); printf("%d", b); return 0;}
4.与函数的区别
从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:
1)宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
2)函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率
三、预处理指令——条件编译
在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译。
1.基本用法
#if 条件1...code1...// 代码段1#elif 条件2...code2...// 代码段2#else...code3...// 代码段3#endif
1>如果条件1成立,那么编译器就会把#if与 #elif之间的code1代码编译进去(注意:是编译进去,不是执行,很平时用的if-else是不一样的)
2>如果条件1不成立、条件2成立,那么编译器就会把#elif与 #else之间的code2代码编译进去
3>如果条件1、2都不成立,那么编译器就会把#else与 #endif之间的code3编译进去
4>注意,条件编译结束后,要在最后面加一个#endif,不然后果很严重(自己思考一下后果)
5> #if和 #elif后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才产生的、才有使用的意义
2.举个例子
#include <stdio.h>#define MAX 11int main (){#if MAX == 0 printf("MAX是0");#elif MAX > 0 printf("MAX大于0");#else printf("MAX小于0");#endif return 0;}
在第3行定义了一个宏MAX,当然在开发中这个MAX可能被定义在其他头文件中,现在只是为了方便演示,就写到main函数上面了。注意第7到第13行的条件编译语句。
由于MAX为11,所以#elif的条件成立,第10行代码将会被编译进去,其实编译预处理后的代码是这样的:
/*stdio.h文件中的内容将会代替#include <stdio.h>的位置*/ int main () { printf("MAX大于0"); return 0; }
3.其他用法
1)#if defined()和#if !defined()的用法
// #if 和 #elif后面的条件不仅仅可以用来判断宏的值,还可以判断是否定义过某个宏。比如:#if defined(MAX) ...code...#endif// 如果前面已经定义过MAX这个宏,就将code编译进去。它不会管MAX的值是多少,只要定义过MAX,条件就成立。// 条件也可以取反:#if !defined(MAX) ...code...#endif// 如果前面没有定义过MAX这个宏,就将code编译进去。
tips:#endif 很重要,如果不写后果很严重。是什么后果?自己想一下吧。
2)#ifdef和#ifndef的使用
#ifdef的使用和#if defined()的用法基本一致
#ifdef MAX ...code...#endif// 如果前面已经定义过MAX这个宏,就将code编译进去。
#ifndef又和#if !defined()的用法基本一致
#ifndef MAX ...code...#endif// 如果前面没有定义过MAX这个宏,就将code编译进去。
四、预处理指令——文件包含
1.基本概念
其实我们早就有接触文件包含这个指令了,就是#include,它可以将一个文件的全部内容拷贝另一个文件中。
2.一般形式
直接到C语言库函数头文件所在的目录中寻找文件
系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找
3.使用注意
1)#include指令允许嵌套包含,比如a.h包含b.h,b.h包含c.h,但是不允许递归包含,比如 a.h 包含 b.h,b.h包含 a.h。
2)使用#include指令可能导致多次包含同一个头文件,降低编译效率
例如:one.h文件中声明了one函数;two.h文件中文件包含了one.h,并且声明了two函数;预编译过后,main.c文件中的代码是这样的:
#include <stdio.h>// 第3行是原来的 #include "one.h"void one();// 第5、6行是原来的 #include "two.h"void one();void two();int main(){ one(); two(); return 0;}这样的结果是,one函数被声明了两次,虽然编译器不会报错,但是降低了编译效率。
那么,如何解决这个问题呢?
一般,我们会这样写头文件的内容:
这样写头文件后,main.c文件的代码效果相当于:
能想到这样写之后的效果么?当预编译时,由于第4行代码做过了#define _ONE_H_ 这个操作,13至18行代码就不会被编译,这样就达到了我们想要的效果:即便two.h中嵌套了one.h,也不会导致one.h被重复包含。
tips:
一般,用于防止头文件被重复包含所写的#ifdef、#define 后面的字符串,我们保持与本文件名一致,前后加下划线。比如上面的 #define _ONE_H_
小结:
1.带参数的宏比函数具有更高的执行效率。
2.宏定义仅仅是文字替换,为了避免计算结果不正确,宏的参数要加上括号。
3.条件编译要记住写 #endif ,不然后果很严重。
4.可以嵌套包含(b.h包含a.h,c.h包含b.h),但是不能递归包含(a.h包含b.h,b.h包含a.h)。
4.为了防止我们写的头文件被重复包含,注意头文件的书写规范
#ifndef _XXX_H_
#define _XXX_H_
…code…
…code…
#endif
To be continue……
- 黑马程序员——零基础学习iOS开发——09 预处理指令
- 黑马程序员——C语言基础---预处理指令
- 黑马程序员——零基础学习iOS开发——01前言
- 黑马程序员——零基础学习iOS开发——02学前准备
- 黑马程序员——零基础学习iOS开发——08 结构体、枚举、typedef
- 黑马程序员——零基础学习iOS开发——11 OC内存管理
- 黑马程序员——零基础学习iOS开发——12 Object-C block、protocol
- 黑马程序员——零基础学习iOS开发——13 Foundation框架
- 黑马程序员之ios学习总结——07 C语言的枚举、预处理指令
- 黑马程序员——零基础学习iOS开发——04 c语言:基本运算、流程控制、函数
- 黑马程序员——零基础学习iOS开发——07 全局变量、局部变量、static和extern关键字
- 黑马程序员——零基础学习iOS开发——14 对继承、分类、协议的分析
- 黑马程序员——ios开发基础之C语言预处理命令与文件操作
- 黑马程序员——C语言基础---预处理指令及其他
- 黑马程序员——零基础学习iOS开发——03 c语言基础语法:关键字、标示符、注释、数据结构、变量、变量内存分析、scanf函数
- 黑马程序员——C语言——预处理指令
- 黑马程序员——C语言——预处理指令
- 黑马程序员——预处理指令(宏定义)
- Java EE CDI bean生命周期介绍
- Inside TCP/IP 读书记录
- [刷题]Search Range in Binary Search Tree
- 总结状态栏与导航栏设置
- 对指令(directives)的认识
- 黑马程序员——零基础学习iOS开发——09 预处理指令
- FFMPEG高级编程第一篇:环境搭建及编译
- php常用字符串处理函数
- 时间序列的归一化方法
- 2015年腾讯安全技术笔试经验分享
- 创建第一个SPA应用--2015-03-30记录
- 并查集 Table问题
- FZU 2039-Pets(二分图_最大匹配)
- LBS的球面距离计算及Geohash方案探讨(LBS之一)