概述

大家都知道一个C程序的运行包括编译和链接两个阶段,其实在编译之前预处理器首先要进行预处理操作,将处理完产生的一个新的源文件进行编译。由于预处理指令是在编译之前就进行了,因此很多时候它要比在程序运行时进行操作效率高。在C语言中包括三类预处理指令,今天将一一介绍:

  1. 宏定义
  2. 条件编译
  3. 文件包含

宏定义

对于程序中经常用到的一些常量或者简短的函数我们通常使用宏定义来处理,这样做的好处是对于程序中所有的配置我们可以统一在宏定义中进行管理,而且由于宏定义是在程序编译之前进行替换相比定义成全局变量或函数效率更高。

//
// main.c
// Pretreatment
//
// Created by Kenshin Cui on 14-6-28.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
// #include <stdio.h>
#define PI 3.14 //宏定义一般大写
#define R 10
#define S 2*PI*R //在另一个宏里面引用了上面的宏 int main(int argc, const char * argv[]) {
float r=10.5;
double area=PI*r*r;
printf("area=%.2f\n",area); double a=S;
printf("a=%.2f\n",a);
printf("PI=3.14\n");//注意输出结果不是3.14=3.14而是PI=3.14,字符串中的PI并不会被替换
#undef PI //强制终止宏定义,否则它的范围一直到文件结束
int PI=3.1415926;
double area2=PI*r*r;
printf("area2=%.2f\n",area2); return 0;
}

宏定义实际的操作就是在预处理时进行对应替换,这个阶段不管语法是否正确,而且对于字符串中出现的宏名不会进行替换。宏定义的功能事实上是非常强大的,除了简单的常量替换还可以传入参数:

//
// 1.2.c
// Pretreatment
//
// Created by Kenshin Cui on 14-7-17.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
// #include <stdio.h>
#define SUM(a,b) a+b
#define SUB(a,b) (a-b)
#define MUL (a,b) (a*b) //这么定义是错误的,预处理器会认为宏名为”MUL“,替换内容为”(a,b) (a*b)“ int main(int argc, const char * argv[]) { int a=2,b=3,c,d;
c=SUM(a, b);
printf("c=%d\n",c); //结果:c=5
d=SUM(a, b)*2;
printf("d=%d\n"); //结果:8,为什么不是10呢?因为替换后:d=a+b*2也就是2+3*2=8 int e=SUB(b, a)*2;
printf("(b-a)*2=%d\n",e); //结果:2,如果SUB定义时不加括号这里应该是-1 return 0;
}

上面我们可以看出带参数的宏功能很强大,有点类似于函数,同函数不同的是它只是简单的替换,不涉及存储空间分配,参数、返回值等问题,但是由于它在预处理阶段展开,所以一般效率较高。使用带参数的宏需要注意的就是结果最好用括号括起来否则很容易出现问题(在上面的SUM例子中我们应该已经看到了);还有一点就是带参数的宏定义时名称和参数之间不要有空格。

条件编译

条件编译其实就是在编译之前预处理器根据预处理指令判断对应的条件,如果条件满足就将对应的代码编译进去,否则代码就根本不进入编译环节(相当于根本就没有这段代码)。

//
// main.c
// Pretreatment
//
// Created by Kenshin Cui on 14-06-28.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
// #include <stdio.h>
#define COUNT 1 int main(int argc, const char * argv[]) { //判断是否定义了 COUNT 宏
#if defined(COUNT) //等价于:#ifdef COUNT,相反如果判断没有定义过则可以通过#if !defined(COUNT)或者#ifndef COUNT
printf("COUNT defined\n");
#endif //判断宏定义COUNT是否等于1
#if COUNT==1
showMessage("hello,world!\n");
#else
say();
#endif return 0;
}

文件包含

文件包含指令#include在前面也多次使用过,这里再次强调一下。首先使用#include“xxx”包含和使用#include <xxx>包含的不同之处就是使用<>包含时,预处理器会搜索C函数库头文件路径下的文件,而使用“”包含时首先搜索程序所在目录,其次搜索系统Path定义目录,如果还是找不到才会搜索C函数库头文件所在目录。

另外在使用#include的时候我们需要注意包含文件的时候是不能递归包含的,例如a.h文件包含b.h,而b.h就不能再包含a.h了;还有就是重复包含虽然是允许的(这里指的是重复包含头文件)但是这会降低编译性能,不妨看一下下面的例子:

上面有三段代码,在main.c和person.h中都包含了message.h而main.c自身又包含了person.h,这样程序在预处理阶段会对包含内容进行替换,替换后mian.c中包含了两个#include “message.h”虽然没有报错,但这会影响编译的性能,正确的做法应该是这样的:

其实就是用宏定义判断一个宏是否定义了,如果没有定义则会定义这个宏,这样以来如果已经包含过则这个宏定义肯定已经定义过了,即使再包含也不会重新定义了,下面的代码也就不会包含进去。

iOS开发系列--C语言之预处理的更多相关文章

  1. iOS开发系列--C语言之基础知识

    概览 当前移动开发的趋势已经势不可挡,这个系列希望浅谈一下个人对IOS开发的一些见解,这个IOS系列计划从几个角度去说IOS开发: C语言 OC基础 IOS开发(iphone/ipad) Swift ...

  2. iOS开发系列--Swift语言

    概述 Swift是苹果2014年推出的全新的编程语言,它继承了C语言.ObjC的特性,且克服了C语言的兼容性问题.Swift发展过程中不仅保留了ObjC很多语法特性,它也借鉴了多种现代化语言的特点,在 ...

  3. iOS开发系列--C语言之存储方式和作用域

    概述 基本上每种语言都要讨论这个话题,C语言也不例外,因为只有你完全了解每个变量或函数存储方式.作用范围和销毁时间才可能正确的使用这门语言.今天将着重介绍C语言中变量作用范围.存储方式.生命周期.作用 ...

  4. iOS开发系列--C语言之数组和字符串

    概览 数组在C语言中有着特殊的地位,它有很多特性,例如它的存储是连续的,数组的名称就是数组的地址等.而在C语言中是没有String类型的,那么如果要表示一个字符串,就必须使用字符数组.今天主要就介绍如 ...

  5. iOS开发系列--C语言之指针

    概览 指针是C语言的精髓,但是很多初学者往往对于指针的概念并不深刻,以至于学完之后随着时间的推移越来越模糊,感觉指针难以掌握,本文通过简单的例子试图将指针解释清楚,今天的重点有几个方面: 什么是指针 ...

  6. iOS开发系列--C语言之构造类型

    概述 在第一节中我们就提到C语言的构造类型,分为:数组.结构体.枚举.共用体,当然前面数组的内容已经说了很多了,这一节将会重点说一下其他三种类型. 结构体 枚举 共用体 结构体 数组中存储的是一系列相 ...

  7. iOS开发系列文章(持续更新……)

    iOS开发系列的文章,内容循序渐进,包含C语言.ObjC.iOS开发以及日后要写的游戏开发和Swift编程几部分内容.文章会持续更新,希望大家多多关注,如果文章对你有帮助请点赞支持,多谢! 为了方便大 ...

  8. iOS开发系列--Swift进阶

    概述 上一篇文章<iOS开发系列--Swift语言>中对Swift的语法特点以及它和C.ObjC等其他语言的用法区别进行了介绍.当然,这只是Swift的入门基础,但是仅仅了解这些对于使用S ...

  9. iOS开发系列--App扩展开发

    概述 从iOS 8 开始Apple引入了扩展(Extension)用于增强系统应用服务和应用之间的交互.它的出现让自定义键盘.系统分享集成等这些依靠系统服务的开发变成了可能.WWDC 2016上众多更 ...

随机推荐

  1. RabbitMQ的几种应用场景

    RabbitMQ是一个消息代理,从“生产者”接收消息并传递消息至“消费者”,期间可根据规则路由.缓存.持久化消息.“生产者”也即message发送者以下简称P,相对应的“消费者”乃message接收者 ...

  2. 【Android】一道Android OpenGL笔试题

    一道Android OpenGL笔试题 SkySeraph May. 5th 2016 Email:skyseraph00@163.com 更多精彩请直接访问SkySeraph个人站点:www.sky ...

  3. 数组中pop()和reverse()方法调用

    数组的倒序排列,可以采用reverse()和pop()方法进行排列.

  4. 条码固定资产管理PDA应用

    条码固定资产管理解决方案 一.客户挑战与需求 随着企业经营管理的不断升级,固定资产管理的高效化.智能化管理越来越受到企业管理人员的重视.然而,固定资产具有数量大.种类多.价值高.使用周期长.使用地点分 ...

  5. Programming Language A 学习笔记(二)

    1. 语法糖--元组的"名称引用"与"位置引用": (e1,...,en) <=> {1=e1,...,n=en} 类型:t1 * - * tn & ...

  6. java中注解的使用与实例 (二)

    java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解 ...

  7. [资料分享]dubbo视频教程流行版

    一.基础篇 第001节–课程介绍 第01节–使用Dubbo对传统工程进行服务化改造的思路介绍 第02节–使用Dubbo对传统工程进行服务化改造 第03节–ZooKeeper注册中心安装 第04节–使用 ...

  8. 数组排序(java)

    代码如下: public class paixu{ public static void main(String []args){ int a[] = {78,23,56,34,12,45,67,89 ...

  9. Codeforces Round #368 (Div. 2)

    直达–>Codeforces Round #368 (Div. 2) A Brain’s Photos 给你一个NxM的矩阵,一个字母代表一种颜色,如果有”C”,”M”,”Y”三种中任意一种就输 ...

  10. 格式化input输入内容(金额)

    项目中要用到格式化金额输入框,要求每三个数字用逗号分割开. 添加一个directive angular.module('myApp.directives', []) .directive('filte ...