第一点
关于预处理
程序执行的过程
(1)源码.c->(编译)->elf可执行程序
(2)源码.c->(编译)->目标文件.o->(链接)->elf可执行程序
(3)源码.c->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序
(4)源码.c->(预处理)->预处理过的.i源文件->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序
什么是预处理
编译器本身的主要目的是编译源代码,将C的源代码转化成.S的汇编代码。编译器聚焦核心功能后,就剥离出了一些非核心的功能到预处理器去了。预处理器帮编译器做一些编译前的杂事。
常见的预处理
(1)#include(#include <>和#include ""的区别)
如果是系统指定的自带的用<>,如果是自己写的在当前目录下放着用"",如果是自己写的但是集中放在了一起专门存放头文件的目录下,将来在编译器中用-I参数来寻找,这种情况下用<>。
(2)注释 给人看的,编译器不知道注释;
(3)#if #elif #endif #ifdef
#ifdef XXX判定条件成立与否时主要是看XXX这个符号在本语句之前有没有被定义,只要定义了(我们可以直接#define XXX或者#define XXX 12或者#define XXX YYY)这个符号就是成立的。#if (条件表达式),它的判定标准是()中的表达式是否为true还是flase,跟C中的if语句有点像。
(4)宏定义
右边这种带参宏隐患很大,因为运算符优先级的关系,因此在宏定义时,尽量把参数都用括号括起来(左边);
第二点
宏定义实战
MAX宏 :得出两数较大者
预处理后:
SEC_PER_YEAR:表示一年有多少秒
常规表达:这种写法是大家最能想到的,但是是错的,因为数字超过了int类型存储的范围!
正确写法:
带参宏、带参函数、内联函数
带参宏:宏定义,是在调用宏的地方原地展开,没有返回参数和返回值。因此,他的调用开销比较少,而且他是在预处理阶段就完成的,根本不用进入到编译器阶段;这种就适合代码量少(可能一两句的情况)
带参函数:带参函数,是在编译器阶段去识别使用的,编译器会自动的帮我们去做参数的静态类型检查。当有错误时,他会报警告!当调用函数的时候,就转到函数中去执行,等到执行完,就返回到调用的地方。这种调用开销比较大,适合那种代码多,又不影响效率的情况。
内联函数:通过函数定义前加inline关键字去实现;它的本质是函数,所以他有函数所有的优点(编译器给静态类型检查)。同时他又有带参宏的优点(原地展开,调用开销小);
第三点
函数
递归函数
递归函数就是函数中调用了自己本身这个函数的函数,但是递归不等于循环,通常用于解决的问题就是求阶乘、求斐波那契数列;
递归函数原理
递归函数实际上是在栈内存上递归执行的,所以一定要有收敛性,不然的话会栈溢出,最终执行段错误。