函数的调用逻辑
当函数被调用时,就会使用函数的代码生成一个栈帧,然后直接切换到这个栈帧处执行,直到这个栈帧执行完毕被销毁后,才会回到调用处继续向下执行。简单来说就是函数采用了插入式执行的方式。
也正因为采用这样的方式,所以函数调用实际遵循先入后出的原则,即先调用的函数最后结束,后调用的函数先结束,所以main函数成立程序的入口和出口。
任意自定义函数都可以调用除了main函数之外的一切函数,包括自身。(main函数可以调用自身)
函数的声明
函数声明就是将函数的第一行直接拿出来加个分号,他可以拓展函数的作用域,函数声明中不需要在形参列表中写出参数的标识符,只需要声明类型即可:
int add(int,int);
函数的声明其实在前面省略了extern关键字;
编译过程
预编译(以#开头的语句)
#include
这个预编译语句可以直接包含一个文件,他的处理方式时直接将对应的文件的内容展开到调用处,有两种运用方式:
(#include<test.h>):在系统库默认路径下找对应文件,如存在则打开,否则就失败;
(#include "test.h"):先在本地路径下寻找,不存在就去系统库寻找,如存在就打开,否则就失败。
(通常自定义头文件使用的时双引号,库中的头文件使用的时尖括号)
#define(宏定义)
这个预编译语句后面要跟两个字段,其含义是将字段1替换为字段2。替换时遵循全词匹配,且区分大小
写。替换时会忽略单双引号内的内容。它的字段2可以不写,此时表示将字段1替换掉。这种用法通常用作行内注释。
例如:
#define MAX(a,b) (a) >( )b ?( a) :( b)
此时如存在如下调用:,
printf("%d",MAX(8,6));
则会采用两步查找替换:
1先替换参数,将a,b都替换成8,6,得到8>6?8:6
2再将上面得到的式子替换到宏的调用处。
如果不加括号,可能会出现优先级出错的情况,所以宏的完全体需要带括号,使用\可以扩充宏的作用范围,让下一行也属于着个宏。
#if
基本结构是:
#if
// 1
#elif
// 2
#else
// 3
#endif
三个部分的代码只有一个部分会参与编译,其他部分会在预编译阶段直接删
掉。其运作逻辑和 if/else 语句类似,其中, #if 和 #elif 后面可以跟一个数字或者一个 defined 表
达式:
#if defined(VER1_1)
// 1
#else
// 2
#endif
此时,如果宏定义 #define VER1_1 存在,则走1号分支,否则走二号分支,这样就可以通过宏来控制
编译哪段代码。
其中#if可以简写:#if def和#if defined一样
常用的调试宏
__TIME__ :一个字符串,可以得到编译时间 __LINE__ :一个整型,可以得到当前代码的行号 __FILE__ :一个字符串,可以得到当前代码的所在文件的完整路径
编译
编译过程会把预编译完毕的代码通过一系列编译算法处理成汇编代码。在这个过程中,如果出现错误,
则会报出以C开头的错误代码。
※现代语言基本上都是先编译成汇编代码的。
※在一个工程中,每一个 .c 文件都是单独编译的。在单独编译时,如果遇到函数或者全局变量,此时只
需要有声明即可编译通过,在后续的链接阶段才会去兑现声明的具体函数实现或全局变量实体。
汇编
将上一步中每个 .c 生成的汇编代码逐个的生成二进制文件
※这些二进制文件是由 .c 单独编译而成的,叫做静态库,在windows下通常是 .lib 后缀,在LINUX下
通常是 .o 后缀。
链接
这个阶段会将所有的静态库链接生成一个可执行文件( .exe ),在这个阶段,编译时声明的函数和全
局变量就要寻找对应的实体了,如果找不到,则会报出以 LNK 开头的错误代码。
头文件
头文件中存放各种各样的声明和宏定义:
//array.h
extern void initArray(int**, int);
extern void inputArray(int*, int);
extern void outputArray(int*, int);
extern int getArrayElem(int*, int);
extern void destroyArray(int*);
这样,其他文件中只需要 include 这个头文件,就可以直接将这些声明一次性全部拿走。
※一般头文件都会有一个对应的 .c 文件,里面放着的就是这些函数的实现,别人需要使用你的库时,只
需要将你的 .c 和 .h 引入到自己的工程中,然后 include 这个头文件,就能将你 .c 实现的所有功能全
部引入到项目中来了。
static关键字
static关键字有两个作用,其中一个就跟多文件项目有关。
1、static可以修饰一个全局变量或者函数定义,它可以禁止这个全局变量或者函数参与链接。这意味着
即使声明了,其他文件也无法在链接期找到这个函数。即限制了全局变量或函数的作用域。
2、static可以修饰一个局部变量的定义,表示这个局部变量的实际定义域是全局。相当于定义了一个本
函数专属的全局变量。这样的变量叫静态变量,static实际上是修改了这个变量的定义域。