IOS开发之路--C语言预处理

概述

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

宏定义 条件编译 文件包含

宏定义

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

//
// 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”虽然没有报错,但这会影响编译的性能,正确的做法应该是这样的:

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

(0)

相关推荐

  • 详解C语言编程中预处理器的用法

    预处理最大的标志便是大写,虽然这不是标准,但请你在使用的时候大写,为了自己,也为了后人. 预处理器在一般看来,用得最多的还是宏,这里总结一下预处理器的用法. #include <stdio.h> #define MACRO_OF_MINE #ifdef MACRO_OF_MINE #else #endif 上述五个预处理是最常看见的,第一个代表着包含一个头文件,可以理解为没有它很多功能都无法使用,例如C语言并没有把输入输入纳入标准当中,而是使用库函数来提供,所以只有包含了stdio.h这个头文

  • 你必须知道的C语言预处理的问题详解

    C语言预处理器执行宏替换.条件编译和文件包含.通常采用以"#"为行首的提示.下面是C语言预处理的应用场合: 1.三字母词(Trigraph Sequences) C源程序的字符集被包含在7位的ASCII字符集中,但是它是ISO 646-1983 Invariant Code Set的超集.为了让程序可以在缩减集(reduced set)中呈现出来,下面的三字母词会被替换成相应的单字符. 三字母词 单字符 ??= # ??/ \ ??' ^ ??( [ ??) ] ??! | ??<

  • IOS开发之路--C语言预处理

    概述 大家都知道一个C程序的运行包括编译和链接两个阶段,其实在编译之前预处理器首先要进行预处理操作,将处理完产生的一个新的源文件进行编译.由于预处理指令是在编译之前就进行了,因此很多时候它要比在程序运行时进行操作效率高.在C语言中包括三类预处理指令,今天将一一介绍: 宏定义 条件编译 文件包含 宏定义 对于程序中经常用到的一些常量或者简短的函数我们通常使用宏定义来处理,这样做的好处是对于程序中所有的配置我们可以统一在宏定义中进行管理,而且由于宏定义是在程序编译之前进行替换相比定义成全局变量或函数

  • IOS开发之路--C语言基础知识

    概览 当前移动开发的趋势已经势不可挡,这个系列希望浅谈一下个人对IOS开发的一些见解,这个IOS系列计划从几个角度去说IOS开发: C语言 OC基础 IOS开发(iphone/ipad) Swift 这么看下去还有大量的内容需要持续补充,但是今天我们从最基础的C语言开始,C语言部分我将分成几个章节去说,今天我们简单看一下C的一些基础知识,更高级的内容我将放到后面的文章中. 今天基础知识分为以下几点内容(注意:循环.条件语句在此不再赘述): Hello World 运行过程 数据类型 运算符 常用

  • IOS开发之路--C语言存储方式和作用域

    概述 基本上每种语言都要讨论这个话题,C语言也不例外,因为只有你完全了解每个变量或函数存储方式.作用范围和销毁时间才可能正确的使用这门语言.今天将着重介绍C语言中变量作用范围.存储方式.生命周期.作用域和可访问性. 变量作用范围 存储方式 可访问性 变量作用范围 在C语言中变量从作用范围包括全局变量和局部变量.全局变量在定义之后所有的函数中均可以使用,只要前面的代码修改了,那么后面的代码中再使用就是修改后的值:局部变量的作用范围一般在一个函数内部(通常在一对大括号{}内),外面的程序无法访问它,

  • IOS开发之路--C语言指针

    概览 指针是C语言的精髓,但是很多初学者往往对于指针的概念并不深刻,以至于学完之后随着时间的推移越来越模糊,感觉指针难以掌握,本文通过简单的例子试图将指针解释清楚,今天的重点有几个方面: 什么是指针 数组和指针 函数指针 什么是指针 存放变量地址的变量我们称之为"指针变量",简单的说变量p中存储的是变量a的地址,那么p就可以称为是指针变量,或者说p指向a.当我们访问a变量的时候其实是程序先根据a取得a对应的地址,再到这个地址对应的存储空间中拿到a的值,这种方式我们称之为"直接

  • IOS开发之路--C语言数组和字符串

    概览 数组在C语言中有着特殊的地位,它有很多特性,例如它的存储是连续的,数组的名称就是数组的地址等.而在C语言中是没有String类型的,那么如果要表示一个字符串,就必须使用字符串数组.今天主要就介绍如下三个方面: 一维数组 多维数组 字符串 一维数组 一维数组操作比较简单,但是需要注意,数组长度必须是固定的,长度不能使用变量进行初始化:如果声明的同时进行赋值则数组长度可以省略,编译器会自动计算数组长度:同时数组不能先声明再一次性赋值(当然可以对每个元素一一赋值). #include <stdi

  • IOS开发之路--C语言构造类型

    概述 在第一节中我们就提到C语言的构造类型,分为:数组.结构体.枚举.共用体,当然前面数组的内容已经说了很多了,这一节将会重点说一下其他三种类型. 结构体 枚举 共用体 结构体 数组中存储的是一系列相同的数据类型,那么如果想让一个变量存储不同的数据类型就要使用结构体,结构体定义类似于C++.C#.Java等高级语言中类的定义,但事实上它们又有着很大的区别.结构体是一种类型,并非一个变量,只是这种类型可以由其他C语言基本类型共同组成. // // main.c // ConstructedType

  • iOS开发基础之C语言

    为何学习iOS开发要先学C语言呢,学习c语言对ios开发有何作用. 现在越来越多的iOS开发兴趣爱好者投入到了iOS培训中,有的是已经在职的员工,有的是还在就读的学生,还有一些是完全零基础的同学,那么对于他们来说就要一切从基础开始学,首先从C语言学起. 因为C语言是一门语言基础,因具备简洁.灵活等特点而被广泛应用,在多种开发环境中都起着重要作用.那么,为什么学习iOS开发要先学C语言呢? 因为在iOS开发中,会经常使用C语言和汇编的,而汇编语言相对而言要生涩繁琐些,C语言因此便成为程序员的首选.

  • iOS开发之路--微博“更多”页面

    最终效果图: MoreViewController.m // // MoreViewController.m // 20_帅哥no微博 // // Created by beyond on 14-8-4. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "MoreViewController.h" @interface MoreViewController () { // more.plist根是字典

  • iOS开发之路--微博新特性页面

    BeyondAppDelegate.m // // BeyondAppDelegate.m // 20_帅哥no微博 // // Created by beyond on 14-8-3. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "BeyondAppDelegate.h" #import "BeyondViewController.h" #import "NewFe

  • iOS开发之路--微博骨架搭建

    最终效果图: BeyondViewController.m // // BeyondViewController.m // 20_帅哥no微博 // // Created by beyond on 14-8-3. // Copyright (c) 2014年 com.beyond. All rights reserved. // 这个就是主控制器,分为两块,下面是Dock栏,上面是显示不同的子控制器的view,子控制器最好用导航控制器包装一下,这样子控制器就自带了导航条,左按钮,标题,右按钮 /

随机推荐