邯郸广告公司网站建设,网站建设项目方案ppt,网站主页与导航栏的设计,织梦网站地图怎么做xml1. 预定义符号 C语言设置了一些预定义符号#xff0c;可以直接使用#xff0c;预定义符号也是在预处理期间处理的 __FILE__ 进行编译的源文件 __LINE__ 文件当前的行号 __DATE__ 文件被编译的日期 __TIME__ 文件被编译的时间 __STDC__ 如果编译器遵循ANSI C#xff0c;…1. 预定义符号 C语言设置了一些预定义符号可以直接使用预定义符号也是在预处理期间处理的 __FILE__ 进行编译的源文件 __LINE__ 文件当前的行号 __DATE__ 文件被编译的日期 __TIME__ 文件被编译的时间 __STDC__ 如果编译器遵循ANSI C其值为1否则未定义 举个例子 这行代码的出现位置是在第16行所以__LINE__的值就是16注意这个词是两个下划线LINE两个下划线文件名行号日期时间都是编译当前文件当前位置当前时间的信息注意是编译时的不是运行时的信息。 2. #define定义常量 基本语法 #define name stuff #define定义的stuff内容可以是数值参数甚至是语句举个例子 最后一段定义中我们定义了一个打印的语句很明显我是将一行代码写成了3行。这是因为写成一行代码的时候代码量太长因此我们可以借助续行符进行换行接着写。前两行结尾处的反斜杠就是续行符我们可以理解成将反斜杠后面的回车转义掉了所以回车换行相当于没换视觉上像是换了行但实际上还是在这一行上书写 现在我们思考一个问题在使用#define定义标识符的时候要不要在最后加上 ; 我建议是不要加上的因为#define是暴力替换掉常量名如果不注意的话很容易出问题 观察num被替换之后的样子我们可以发现加 ; 的危害了 3. #define定义宏 于定义常量类似定义宏也是将名暴力替换掉只不过宏新增了一个参数的机制 下面是宏的声明方式 #define name(parament-list) stuff parament-list参数列表是一个由符号隔开的符号表其中的符号可能出现在stuff中 注意参数列表的左括号必须和name紧邻如果两者间由任何空白的存在参数列表就会被认为是stuff的一部分 举例如何使用定义宏 我用宏实现了一个将参数变成其二倍的功能 但是这么写宏是有问题的 我在宏的参数部分写21本意是计算DOUBLE(3)但是因为暴力替换的问题表达式并没有按照我预想的循序计算而计算的是2*21 因此我们在定义宏的时候要将表达式中的元素用小括号括起来保证它们的运算顺序不会在暴力替换之后改变 但是问题还没有解决 很明显这段代码也因为暴力替换的问题本想计算5*4但是实际上计算的是5*22 因此我们要将整体也用小括号固定计算顺序 这样才算正确的定义宏 4. 带有副作用的宏参数 当宏参数在宏定义中出现超过一次的时候如果参数带有副作用那么你在使用这个宏的时候就可能出现危险导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。 x1 不带有副作用 x 带有副作用 举一个副作用的例子 很明显结果和我们预期的不一样判断3和5的大小最后答案竟然是6而且a和b的值也被改变这正是因为参数的副作用带来的问题简单分析一下注释掉的那条语句就可以知道到底是怎么回事了 5. 宏的替换 我们之前已经提到过多次了宏在使用时其实就是将名强行替换掉了那么具体的替换方案涉及以下几个步骤 1.在调用宏时首先对参数进行检查看看是否包含任何由#define定义的符号。如果是它们首先被替换 2.替换文本随后被插入到程序中原来文本的位置。对于宏参数名被它们的值替换。 3.最后再对结果文件进行扫描看看它是否包含任何由#define定义的符号。如果是重复以上处理步骤 注意 1.宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏不能出现递归 2.当预处理器搜索#define定义的符号的时候字符串常量的内容不被搜索 6. 宏和函数的对比 宏通常被应用于执行简单的运算函数用于解决复杂的功能 比如在执行上面那个比较两个数大小的功能的时候写成宏更有优势 宏的优势 1. 进行简单计算的时候需要的计算量比函数更小宏没有像函数的调用和返回那样复杂的汇编语句需要因此速度也会比函数快 2. 宏是与类型无关的所以参数想传什么就传什么具体产生什么效果等替换完了再看。甚至宏可以传递类型名这是函数绝对做不到的 宏的劣势 1. 每次使用宏的时候一份宏定义的代码将插到程序当中去。除非宏比较短否则可能大幅增加宏的长度 2. 宏是没法调试的 3. 宏由于类型无关因此不够严谨 4. 宏可能会带来运算符优先级的问题导致容易出错
7. #和##
7.1 #运算符 #运算符将宏的一个参数转换成字符串字面量。它仅允许出现在带参数的宏的替换宏的替换列表中 #执行的操作可以理解为字符串化 举个例子 这段代码中#n在替换时发生字符串化将 a 变成了 a 当然在观察注释的时候可以注意到printf中包含了3段字符串这种写法是允许的它和第二行注释是等价的 7.2 ##运算符 ##可以把位于它两边的符号合成一个符号它允许宏定义从分离的文本片段创建标识符。##被称为记号粘合 这样的连接必须是产生一个合法的标识符否则结果是未定义的 下面我们举个例子 当我们想比较两个数的大小的时候不同的类型int、float、double都要写一个不同名函数来运算但其实它们的运算内容方法是一样的所以能不能有一种方法把它们整合一下 ##在这段代码中就起到了粘贴 type 和 _max 的作用使得type会被替换之后还能与_max形成新的函数名用这种方法通过一段代码生成了3个功能相似函数名不同的函数 8. 命名约定 一般来讲函数和宏的使用语法很相似所以为了区分二者我们有这样的书写习惯 把宏全部大写 函数名不要全部大写 9. #undef 这条指令用于移除一个宏定义 举个例子 在宏定义移除之后MAX就不能用了但是我们可以用#define重新启用他起到一个修改宏的作用 10. 命令行定义 许多C的编译器提供了一种能力允许在命令行中定义符号用于启动编译的过程 举个例子如果我在编写代码的时候不能确定一个数组要分配多大的空间只能在运行前的时候确定可以这样先把程序写出来 这样在程序中并不指定SIZE的具体大小在启动程序的时候用命令行 gcc -D SIZE10 programme.c 这样程序就能正常跑起来并且开辟一个10个元素的数组 这个功能vs中不好演示感兴趣可以用Linux尝试一下 11. 条件编译 在编译一个程序的时候我们可以使用条件编译指令来控制要不要编译这条语句 比如说那些调试性的代码删除可惜保留又碍事所以我们可以选择性编译 常见的条件编译指令 1.#if #endif #if 常量表达式(为真编译为假不编译) ······ #endif 注意#if后面跟的是常量表达式 这么写就是有错误的。第一#if 后边要求跟常量表达式a是变量。第二#if 是在预处理阶段进行处理的这时候a还没有定义呢那a算什么 2. 多分支的条件编译 #if 常量表达式 ······ #elif 常量表达式 ······ #else ······ #endif 3. 判断是否被定义 #if defined(symbol) #ifdef symbol 这两条语句都是说如果这个symbol被定义了就编译下面的代码 #if !defined(symbol) #ifndef symbol 这两条语句是说如果没定义symbol就编译下面的代码 当然不要忘记 #endif 结束条件编译#endif和距它最近的 #if 或 #if··· 匹配不管是 #if 还是 #if··· 都要记得用 #endif 结束 4.条件编译指令是可以嵌套的 12. 头文件的包含
12.1 头文件被包含的方式
12.1.1 本地文件包含 #include filename.h 查找策略现在源文件目录下查找如果该头文件未找到编译器就像查找库函数头文件一样在标准位置查找头文件如果还是找不到就报错
12.1.2 库文件包含 #include filename.h 查找这种类型的头文件就直接去标准路径下查找(所以库函数所在位置)如果找不到就报错 这样是不是可以说对于库函数包含的时候可不可以用 的形式 确实是可以的但是这样做效率就会低一点同时这样也不容易区分我包含的是库文件还是本地文件 12.2 嵌套文件包含 我们之前已经学到过#include就是把另一个文件复制到这条语句所在位置并替换掉这条语句。 那么在一个大型的项目中在所难免的会出现头文件被重复包含的现象这会导致多次把头文件中的内容粘过来如果头文件过大这很影响编译的速度 所以我们可以用条件编译解决这个问题在头文件开头写 这样就可以通过判断__TEST_H__是否被定义来确定这个头文件有没有使用过从而确定要不要把头文件的内容粘过去 还有一种方法在头文件开头加上 就可以避免头文件的重复引用这个方法是比较常见的 13. 其他预处理指令 #error #pragma #line #pragma pack() 修改默认对齐数在结构体中有讲 ······ 更多内容可以参考《C语言深度解剖》中预处理一章