【C语言学习笔记】《C程序设计语言》 第4章(函数与程序结构)——第3节(递归、C预处理器)

Warning:
为了避免非零基础人群感到身体不适、头晕恶心、易怒及粗口,请不要查看以下内容。

今天我们要结束这一章,简单的介绍几个C语言中常见的概念和用法。

第4章 函数与程序结构

4.10 递归

C语言中的函数可以递归调用,即函数可以直接或间接调用自身。

直接调用自己称为直接递归,间接调用自己称为间接递归

递归并不节省存储器的开销,因为递归调用过程中必须在某个地方维护一个存储处理值的栈。递归的执行速度并不快,但递归代码比较紧凑,并且比相应的非递归代码更易于编写与理解。在描述树等递归定义的数据结构时使用递归尤其方便。

4.11 C预处理器

C语言通过预处理器提供了一些语言功能。从概念上讲,预处理器是编译过程中单独执行的第一个步骤。两个最常用的预处理器指令是:include指令(用于在编译期间把指定文件的内容包含进当前文件中)和#define指令(用任意字符序列替代一个标记)。本节还将介绍预处理器的其它一些特性,如条件编译与带参数的宏。

4.11.1 文件包含

文件包含指令(即#include指令)使得处理大量的#define指令以及声明更加方便。在源文件中,任何形如:

#include "文件名"#include <文件名>

的行都将被替换为由文件名指定的文件的内容。如果文件名用引号引起来,则在源文件所在的位置查找该文件;如果在该位置没有找到该文件,或者如果文件名是用尖括号括起来的,则将根据相应的规则查找该文件,这个规则同具体的实现有关。被包含的文件本身也可包含#include指令。

源文件的开始处通常都会有多个#include指令,它们用以包含常见的#define语句和extern声明,或从头文件中访问库函数的函数原型声明,比如<stdio.h>(严格的说,这些内容没有必要单独存放在文件中;访问头文件的细节同具体实现有关)

4.11.2 宏替换

宏定义的形式如下:

#define 名字 替换文本

这是一种简单的宏替换——后续所有出现名字标记的地方都将被替换为替换文本。#define指令中的名字与变量名的命名方式相同。替换文本可以是任意字符串。通常情况下#define指令占一行,替换文本是#define指令行末尾的所有剩余内容,但也可把一个较长的宏定义分成若干行,这时需要在待续的行末尾上加一个反斜杠符"\"。#define指令定义的名字的作用域从其定义点开始,到被编译的源文件的末尾处结束。宏定义中也可以用前面出现的宏定义。替换只对记号进行,对括在引号里的字符串不起作用。

可以使用#undef指令取消名字的宏定义,这样做可以保证后续的调用是函数调用,而不是宏调用。

预处理器运算符##为宏拓展提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白符将被删除。并对替换后的结果重新扫描。如:

#define paste(front, back) front ## back

因此,宏调用paste(name, 1)的结果将建立记号name1。

4.11.3 条件包含

还可以使用条件语句对预处理本身进行控制,这种条件语句的值是在预处理执行的过程中进行计算。这种方式为在编译过程中根据计算所得的条件值选择性的包含不同代码提供了一种手段。

#if语句对其中的常量整型表达式(其中不能包含sizeof、类型转换符或enum常量)进行求值,若该表达式的值不为0,则包含其后的各行,直到遇到#endif、#elif或#else语句为止(预处理器语句#elif类似于else if)。在#if语句中可以使用表达式defined(名字),该表达式的值遵循下列规则:当名已定义时,其值为1;否则,其值为0。

C语言专门定义了两个预处理器语句#ifdef与#ifndef,它们用来测试某个名字是否已经定义。

总结

这一章终于结束了,在预处理器阶段讲的可能不太明白,后期在实践中应该多注意。感觉本书这部分的翻译,或者说是结构安排有点问题…至少我是看不懂,晕了(大神无视)。这部分不影响我们后续的学习,以后有机会可以深入研究。下一章我们就开始接触C语言中最难的部分 指针与数组。学完指针与数组,就基本结束了C语言基础知识的学习,把基础知识熟练掌握后,我们会进行相应的深入研究和实践练习。

发布了14 篇原创文章 · 获赞 0 · 访问量 494

猜你喜欢

转载自blog.csdn.net/AtomTeam/article/details/104268845