一、函数的声明
(1)什么是函数的声明
告诉编译器函数返回类型、函数名、参数。
(2)单个文件的声明
当你的自定义函数写在main函数之前时,则编译器会默认你已经声明过了;
当你的自定义函数写在main函数之后时,则需要在main函数之前进行声明。
cpp
//函数在单个文件中的声明
int Add(int a, int b);//自定义函数在main函数之后,需要声明
int main() {
int ret = Add(3, 5);
printf("%d\n",ret);
}
int Add(int a, int b) {
return a + b;
}
(2)多个文件的声明
①使用
创建3个文件,一个.c文件用来写你的主代码块;一个.c文件用来写你的函数实现;一个.h文件用来存放你的函数声明。(使用时要包含你的自定义头文件)
cpp
//主函数部分 test.c
#include <stdio.h>
#include "calc.h"
int main() {
printf("%d\n", Add(9, 3));
printf("%d\n", Sub(9, 3));
printf("%d\n", Mul(9, 3));
printf("%d\n", Div(9, 3));
}
//函数实现部分 calc.h
int Add(int a, int b) {
return a + b;
}
int Sub(int a, int b) {
return a - b;
}
int Mul(int a, int b) {
return a * b;
}
int Div(int a, int b) {
return a / b;
}
//函数声明部分 calc.h
int Add(int a, int b);
int Sub(int a, int b);
int Mul(int a, int b);
int Div(int a, int b);
②好处
逻辑清晰;方便多人协作。
二、static和extern
关键字之一,并不是函数,不需要包含头文件。
static用于延长生命周期(修饰局部变量时,由函数栈帧范围延长为整个程序),收窄作用域(修饰函数和全局变量时,由多个文件收窄到当前文件)。
extren用于声明外部符号。
(1)作用域
限定变量的可用范围。
cpp
#include <stdio.h>
int main() {
{
int a = 3;
printf("%d\n", a);//代码正常执行,a的使用范围在这个大括号内
}
printf("%d\n", a);//代码无法执行,超出了a的作用域,无法使用
}
(2)生命周期
变量从创建(申请内存)到销毁(收回内存)的过程。
(3)内存划分图

(4)static修饰局部变量
static修饰的局部变量存储在静态区,改变了局部变量的生命周期,程序运行结束,局部变量的声明周期才结束,但是作用域不发生变化。
cpp
void test() {
static int i = 0;//仅在第一次使用test函数时执行这一行
i++;
printf("%d ",i);
}
#include <stdio.h>
int main() {
for (int i = 0; i < 5; i++)
test();//打印 1 2 3 4 5
}
(5)extern声明全局变量
cpp
//testo1.c
int a = 18;
//test02.c
//extern
extern a;
#include <stdio.h>
int main() {
printf("%d\n", a);//extern声明外部符号a后,a就可以正常使用了
}
(6)static修饰全局变量
使全局变量失去外部链接性。
cpp
//test01.c
static int a = 18;
//static修饰后,这个文件里的全局变量就失去了外部链接属性,只能在这个文件里使用
//test02.c
extern a;
#include <stdio.h>
int main() {
printf("%d\n", a);//全局变量a被static修饰了,即使声明也不能再次使用了
}
(7)extern声明函数
cpp
//test01.c
int Add(int a, int b) {
return a + b;
}
//test02.c
extern Add(int a, int b);
#include <stdio.h>
int main() {
printf("%d\n", Add(2,3));//声明外部函数后,可以正常使用
}
(8)static声明函数
使函数失去外部链接属性。
cpp
//testo1.c
static int Add(int a, int b) {
return a + b;
}
//test02.c
extern Add(int a, int b);
#include <stdio.h>
int main() {
printf("%d\n", Add(2,3));//static修饰的外部函数,已经失去了外部链接属性
}
三、函数栈帧
(1)内存
①定义
内存(RAM)就是用来存储临时数据的硬件,对于数据而言是一片空间。
②构成
栈区:函数栈帧(局部变量、函数参数、返回地址)、寄存器(例如ebp:基址指针寄存器,存放指向当前函数栈帧栈底的指针;esp:栈指针寄存器,存放指向当前函数栈帧栈顶的指针)
堆区:动态分配的内存(malloc/calloc/realloc申请的空间)
静态区:全局变量、static修饰的变量
常量区:字符串常量
代码区:程序的二进制指令
(2)寄存器
寄存器其实也是一片空间,用来临时存储cpu正在运算的数据。
(3)函数栈帧的创建和销毁过程
栈帧创建:把返回地址和旧栈底存起来,用两个寄存器定好当前栈帧的头尾,腾出空间放局部变量。
栈帧销毁:把计算结果存好,恢复之前的栈底和栈顶位置,跳回原来的函数继续执行,当前函数的栈帧随之销毁。