[精通Objective-C]预处理器

[精通Objective-C]预处理器

参考书籍:《精通Objective-C》【美】 Keith Lee

目录

  • 精通Objective-C预处理器

    • 目录
    • 预处理器概述
    • 预处理器语言
      • 预处理器指令

预处理器概述

预处理根据一系列预定义规则,使用一些字符序列替换输入的字符序列。这些操作主要分为以下三步:

Created with Rapha?l 2.1.0输入源文件执行文本翻译将输入的源文件拆分成多个记号将输入代码转换为预处理器语言

1.文本翻译:预处理会将输入的源文件拆分成代码行、使用单个字符替换三字母组合、将被断开的连续行合并为较长的代码行和使用单个空格替换注释。三字母组合是指C语言中用来代表单个字符的三字符序列。

2.记号转换:预处理器将上一步骤处理过的代码转换为记号序列。

3.基于预处理器语言的转换:如果记号序列中含有预处理语言元素,就根据这些记号进行转换。

前两个操作是自动执行的,而最后一个操作是由添加到源文件中的预处理器语言函数执行的。

预处理器语言

预处理器语言是一门完全独立的编程语言。预处理语言对源文件进行的转换主要包括源文件的内容,条件编译和宏展开。预处理器语言元素会在程序编译前处理源文件,但预处理器不能识别Objective-C代码。

预处理器指令

预处理器指令格式:

#指令名 指令参数

预处理器指令以#开头,后面紧跟指令名,之后是相应的参数:

#import "Atom.h"

预处理器指令会将换行符号用作结束符号。要使预处理器指令扩展为多行,可使用反斜杠\连接两行代码:

#define DegreesToRadians(x) \
        ((x)*3,14159/180)

预处理器指令主要包括了以下4类:头文件包含,条件编译,诊断,#pragma指令

头文件包含类指令有#include和#import

#include <Foundation/Foundation.h>
#include "Atom.h"
#import <Foundation/Foundation.h>
#import "Atom.h"

双引号和尖括号的区别在于,使用双引号时,编译器会先从存储源文件的目录中搜索被包含的头文件。如果没有找到,编译器会在默认目录中搜索头文件,默认目录是预先配置的用于搜索系统标准头文件的目录;而使用尖括号时,编译器会直接在默认目录中搜索被包含的头文件。按惯例,应使用尖括号封装标准头文件,而其他文件用双引号封装。

而#import与#include的区别在于,#import可确保头文件仅在源文件中被包含一次,因而能够防止递归包含。例如之前章节([精通Objective-C]对象和消息传递)中,源文件main.m包含了头文件Hydrogen.h和Atom+Nuclear.h,这两个文件又都包含了头文件Atom.h,通过#import指令包含头文件Hydrogen.h和Atom+Nuclear.h就可以让main.m只包含头文件Atom.h一次,如果使用#include的话,就需要在头文件Atom.h中添加包含警卫:

#ifndef ATOM_H
#define ATOM_H
@interface Atom : NSObject
......
@end
#endif

条件编译指令有#if、#elif、#else、#endif、#ifdef和#ifndef可以根据条件是否成立,确定包含或不包含部分或者全部源文本。

条件编译指令#if、#elif和#else的用法与Objective-C中的if、else if和else类似,只是结束整个条件语句时要加上#endif。同样地#if和#endif也可以嵌套使用。

//预处理器会展开INPUT_ARGS标识符,如果该标识符是一条宏命令,它就会被相应的值替换,如果不是或者这个宏没有值,则被替换为0
#if INPUT_ARGS <= 0
#warning "No input arguments defined"
#elif INPUT_ARGS > 100
#error "Input arguments are too many"
#else
#define Sum INPUT_ARGS
#endif

之前的包含警卫就是使用的#ifdef和#ifndef

#ifndef ATOM_H
#define ATOM_H
@interface Atom : NSObject
......
@end
#endif

等价于

#if !defined ATOM_H
#define ATOM_H
@interface Atom : NSObject
......
@end
#endif

诊断类预处理器指令有#warning、#error和#line。

以下是#warning和#error的用法:

#warning "No input arguments defined"
#error "Input arguments are too many"

而#line的语法为

#line 行号 "文件名"

该行号将被赋予下一行代码,而后续的代码行都会拥有每行加1的行号,当编译出错时,编译器会显示含有出错文件名称和相应行号的错误信心。由于大多数编译工具都能够显示源文件的行号,以及错误和警告信息,因此很少需要用到#line。

添加#pragma指令可以在弹出窗口设置标签

//在弹出窗口中设置创建分割线
#pragma mark -
//在弹出窗口中设置标签名称
#pragma mark Main
int main(int argc, const char * argv[]) {
    @autoreleasepool {
    }
    return 0;
}
#pragma mark -

效果如下所示,可以在标注位置通过选择标签跳转到指定位置,这在大型工程和类中尤为有用:

宏是具有名称的代码段。当在源代码中使用某个名称时,其代表的代码段就会替换它。只用预处理器宏指令可以定义常量值,还可以配合输入参数值提供类似函数的功能。使用#define定义宏,#undef移除宏。

宏只能执行简单的替换,所以在使用宏时要一定要小心。以下面的一个计算平方的宏为例

#define SQUARE(x) x * x
int result = SQUARE(4 + 2);

得到的结果不是36,而是是14,因为int result = SQUARE(4 + 2);等价于int result = 4 + 2 * 4 + 2;

如果要想得到我们想要的结果应该写成:

#define SQUARE(x) ((x) * (x))

实际上,函数型宏和对象型宏的定义中都应该使用括号,另外,应使用花括号封装用于执行而非返回值的多行函数型宏:

#define SWAP(a,b) {a^=b; b^=a; a^=b;}

宏具有强大的功能,但是非常依赖复杂宏的代码会难以维护,因此,不要过度使用宏!

时间: 2024-08-01 23:00:24

[精通Objective-C]预处理器的相关文章

【转载】GCC 预处理器选项

预处理器选项(Preprocessor Option) 下列选项针对C预处理器,预处理器用在正式编译以前,对C 源文件进行某种处理. 如果指定了`-E'选项, GCC只进行预处理工作.下面的某些选项必须和`-E'选项一起才 有意义,因为他们的输出结果不能用于编译. -include file 在处理常规输入文件之前,首先处理文件file,其结果是,文件file的内容先得到编译. 命令行上任何`-D'和`-U'选项永远在`-include file'之前处理, 无论他们在命令行上的顺序如何.然而`

C预处理器

C预处理器是一种简单的宏处理器. 预处理器是由特殊的预处理器命令行控制的,它们是以#符号开头的源文件行. 预处理器的一般操作:从源文件中删除所有的预处理器命令行,并在源文件中执行这些预处理器命令所指定的转换操作 预处理器代码行的语法与C语言其他部分的语法是完全独立的,但经过预处理所产生的源代码必须在上下文环境中合法 常见的预处理器命令: #define  定义一个预处理器宏   #undef     取消一个预处理器宏 #include   插入另一个源文件的文本 #if        测试一个

笔记3:预处理器-(2)宏定义

#define指令称为宏定义指令,通常用#define指令来定义一个宏用来代表其他东西的一个名字(如常量表达式等).通常来说预处理器会通过将宏的名字和它的定义存储在一起来响应#define指令.当这个宏在后面的程序中使用到时,预处理器会"扩展"宏,将宏替换为其定义值. 简单的宏 简单的宏的定义格式: #define 标识符 替换列表 如: #define DTE_LEN 80 #define TRUE 1 #define FALSE 0 #define PI 3.1415926 #de

第六篇:使用预处理器帮助调试

前言 你是否遇到过以下情况? 情况一:为了调试方便,代码中夹杂各种cout语句.当调试好了,把这些语句删了,运行“正式版”后,又发现新问题,只得把这些cout语句一个个添加回去再进行调试.如此不断循环. 情况二:希望在代码中获取到源文件的文件名,当前代码行号,编译时间等信息. 情况三:纠结于是否实现某些概率极低(几乎不存在)的错误检测 如果有,那么这篇随笔适合你,或者说,预处理器带来的调试技术适合你. 技巧一:设置调试区代码开关 请看下面的源代码: 1 #include <iostream>

关于前端CSS预处理器Sass的小知识!

前面的话 ??"CSS预处理器"(css preprocessor)的基本思想是,用一种专门的编程语言,进行网页样式设计,然后再编译成正常的CSS文件.SASS是一种CSS的开发工具,提 供了许多便利的写法,大大节省了设计者的时间,使得CSS的开发,变得简单和可维护.本文将详细介绍sass的使用 定义 ??Sass是一门高于CSS的元语言,它能用来清晰地.结构化地描述文件样式,有着比普通CSS更加强大的功能.Sass能够提供更简洁.更优雅的语法,同时提供多种功能来创建可维护和管理的样式

预处理器

本文是对C++预处理器的学习整理,参考了网站www.learncpp.com相关章节的内容. 一.概述 代码在编译之前需要通过预处理器进行预处理,预处理器运行时,逐行扫描代码寻找预处理指令.预处理指令是以#开头.换行符结尾(不是分号:)的代码. 预处理器主要实现一下三个功能: 1. include 2. macro define 宏定义. 3. 条件编译 二.include 故名思意,#include用于包含头文件,预处理器在遇到#include 指令时,将相应头文件的内容复制到指令所在的位置.

C语言难点2之预处理器

C语言难点2之预处理器 1 预处理阶段 在预处理阶段中,C预处理器在源代码编译之前对其进行一些文本性质的操作.它的主要任务包括删除注释,插入被#include指定包含的文件的内容,定义和替换由#define指令定义的符号一起确定代码的部分内容是否应该根据一些条件编译指令进行编译. 2 #define命令 采用宏定义时候的易错点: 格式: #define  name  stuff 有了这条指令之后,每当有符号name出现在这条指令后面时,预处理器就会把它替换成stuff. 注意:宏定义后面不能加上

C Primer Plus之C预处理器和C库

编译程序前,先由预处理器检查程序(因此称为预处理器).根据程序中使用的预处理器指令,预处理器用符号缩略语所代表的内容替换程序中的缩略语. 预处理器不能理解C,它一般是接受一些文件并将其转换成其他文本. 翻译程序的第一步 对程序作预处理前,编译器会对它进行几次翻译处理. 编译器首先把源代码中出现的字符映射到源字符集(?).该过程处理多字节字符和使C外观更加国际化的三元字符(?)扩展 编译器查找反斜线(\)后紧跟换行符的实例并删除这些实例.注意:在这种场合下,“换行符”代表按下回车键在源代码文件中新

C Primer Plus读书笔记-预处理器指令

代码中经常看到  #define ,#ifdef #define :明显常量 一般的指令长度仅限与一行代码,除了\(反斜杠线)可以拓展到下一行外. 一般情况下,每个#define行由三个部分组成. 第一部分为#define 指令本身 第二部分为所选择的缩略语,这些缩略语称为宏(macro).  宏的名字中不允许有空格!只能使用字母,下划线(_),和数字之间的组合,第一个字符不能为数字 第三部分(#define行除了第一第二部分外的部分).称为替换列表或主体. 预处理器在程序中发现宏的实例后,总会