网站建设与制作软件,优化师培训机构,网站的总规划书,网站信息内容建设自查报告目录 1.预定义符号
2.#define
2.1#define 定义标识符
2.2#define 定义宏
2.3#define 替换规则
2.4#和##
2.4.1# 的作用
2.4.2## 的作用
2.5 带有副作用的宏参数
2.6宏和函数的对比
对比
**2.7内联函数
2.8命名约定
3.#undef
**4.命令行定义
5.条件编译
常…目录 1.预定义符号
2.#define
2.1#define 定义标识符
2.2#define 定义宏
2.3#define 替换规则
2.4#和##
2.4.1# 的作用
2.4.2## 的作用
2.5 带有副作用的宏参数
2.6宏和函数的对比
对比
**2.7内联函数
2.8命名约定
3.#undef
**4.命令行定义
5.条件编译
常见的条件编译指令
6.头文件包含
6.1头文件被包含的方式
6.2嵌套文件的包含 1.预定义符号 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C其值为1否则未定义 这些预定义符号都是语言内置的
举个例子 2.#define
#define是一种预处理指令他有两种用法
#define 定义常量标识符#define 定义宏
2.1#define 定义标识符 语法 #define name stuff 举个例子 #define 是完全替换比如 所以在定义的时候为了强调他是一个整体需要自己带上括号 注意由于是完全替换在define定义标识符的时候不要在最后加 ; 否则替换的时候会将 ; 也替换过去会导致语法错误
2.2#define 定义宏 #define 机制包括了一个规定允许把参数替换到文本中这种实现通常会被解释为宏macro或定义宏define macro 下面是宏的声明方式
#define name( parament-list ) stuff
其中的parament-list是一个由逗号隔开的符号表他们可能出现在stuff中
注意
参数列表的左括号必须与name紧邻
如果两者之间有任何空白存在参数列表就会被释解释为stuff的一部分
如 #define定义宏也是完全替换比如 为了防止出现失误我们在声明的时候需要加上括号 我们在写宏的时候如果逻辑需要我们可以加上足够多的括号来使宏变得完整
2.3#define 替换规则
在程序中扩展#define定义符号和宏时需要涉及几个步骤
在调用宏时首先对参数进行检查看看是否包含任何由#define定义的符号。如果是他们首先被替换替换文本随后被插入到程序中原来文本的位置。对于宏参数名被他们的值所替换最后再次对结果文件进行扫描看看它是否包含任何由#define定义的符号。如果是就重复上述处理过程
注意
宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏不能出现递归当预处理器搜索#define定义的符号的时候字符串常量的内容并不被搜索
2.4#和##
2.4.1# 的作用
如何把参数插入到字符串中、 我们发现字符串是有自动连接的特点的
假设有这样的代码 我们如何用宏来实现printf的功能呢这里我们使用# 他的替换是周怎么完成的呢 这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中
使用#把一个宏参数变成对应的字符串
比如代码中的#N会被预处理器处理为“N”
所以“#N”即被处理为““N””
2.4.2## 的作用 ##可以把位于他两边的符号合成一个符号 他允许宏定义从分离的文本片段创建标识符 注意这样的连接必须产生一个合法的标识符否则其结果就是未定义的
2.5 带有副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候如果参数带有副作用那么在使用这个宏的时候就可能出现危险导致不可预测的后果。副作用就是表达式求值的时候出现的有永久性效果 x1//不带副作用 x//带副作用 MAX宏可以证明具有副作用的参数所引起的问题 这段代码输出的结果是什么
这里我们得知道预处理之后的结果是什么
这段代码是证明执行的呢 2.6宏和函数的对比
宏通常被应用于执行简单的运算
比如在两个数中找出较大的一个 #define MAX(a, b) ((a)(b)?(a):(b)) 那为什么不用函数来完成这个任务
原因有二
用于调用函数和从函数返回的代码可能实际执行这个小型计算工作所需要的时间更多所以宏比函数在程序的规模和速度方面更胜一筹更为重要的是函数的参数必须声明为特定的类型 所以函数只能在类型合适的表达式上使用宏是类型无关的 宏的缺点当然和函数相比宏也有劣势的地方
每次使用宏的时候一份宏定义的代码将插入到程序中除非宏比较短否则可能大幅度增加程序的长度宏是没法调试的宏由于类型无关也就不够严谨宏可能会带来运算符优先级的问题导致过程容易出现错误 宏有时候可以做函数做不到的事情比如宏的参数可以出现类型但是函数做不到
对比 建议
如果逻辑比较简单可以使用宏来实现
如果计算逻辑比较负责那么就使用函数实现
**2.7内联函数
C99之后C引入了内联函数的概念 inline关键字
内联函数具有函数和宏的双重优点 内联函数是函数内联函数又像宏一样在调用的地方展开 2.8命名约定
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者
那我们平时的一个习惯是 把宏名全部大写 //MAX函数名不要全部大写 //Max 3.#undef
这条指令用于移除一个宏定义 **4.命令行定义
许多C的编译器提供了一种能力允许在命令行中定义符号用于启动编译过程
例如当我们根据同一个源文件要编译出一个程序的不同版本的时候这个特性有点用处
假定某个程序中声明了一个某个长度的数组如果机器内存有限我们需要一个很小的数组但是另外一 个机器内存大写我们需要一个数组能够大写。
5.条件编译
在编译一个程序序的时候我们如果要将一条语句一组语句编译或者放弃是很方便的因为我们有条件编译指令
条件编译就是满足条件就编译不满足条件就不编译
比如说 调试性的代码删除可惜保留又碍事所以我们可以选择性的编译 常见的条件编译指令 1. #if 常量表达式 //... #endif//常量表达式由预处理器求值 如 #define __DEBUG__ 1 #if __DEBUG__ //.. #endif 表达式为真则编译为假则不编译 2.多个分支的条件编译 #if 常量表达式 //... #elif 常量表达式 //... #else //... #endif 只会选择以一个#if或者#elif执行 3.判断是否被定义 #if defined(symbol) #ifdef symbol #if !defined(symbol) #ifndef symbol 判断symbol是否被定义过如果被定义过则执行代码 4.嵌套指令 #if defined(OS_UNIX) #ifdef OPTION1 unix_version_option1(); #endif #ifdef OPTION2 unix_version_option2(); #endif #elif defined(OS_MSDOS) #ifdef OPTION2 msdos_version_option2(); #endif #endif 注意#if 与 #endif 是配套使用的同时出现同时消失
6.头文件包含
我们已经知道#include 指令可以使另外一个文件被编译就像它实际出现于 #include 指令的地方一样
这种替换的方式很简单
预处理器先删除这条指令并用包含文件的内容替换 这样一个源文件被包含10次那就实际被编译10次
6.1头文件被包含的方式
头文件的包含一般有两种方式 1.包含本地文件自己的.h文件 #include xxx.h用双引号 2.包含标准库中的文件 #include xxx.h 用尖括号 查找策略
先在源文件所在目录下查找如果该头文件未找到编译器就像查找库函数头文件一样在标准位置查找头文件
如果找不到就提示编译错误
6.2嵌套文件的包含
如果出现这样的场景 comm.h和comm.c是公共模块 test1.h和test1.c使用了公共模块 test2.h和test2.c使用了公共模块 test.h和test.c使用了test1模块和test2模块。 这样最终程序中就会出现两份comm.h的内容 这样就造成了文件内容的重复
我们可以用条件编译解决这个问题
每个头文件的开头写 #ifndef __TEST_H__ #define __TEST_H__//头文件的内容 #endif //__TEST_H__ 或者 #pragma once 就可以避免头文件的重复引入