一、函数入门
\qquad
在C语言中,函数意味着功能模块。一个典型的C语言程序,就是由一个个的功能模块拼接起来的整体。也因为如此,C语言被称为模块化语言。
对于函数的使用者,可以简单地将函数理解为一个黑箱,使用者只管按照规定给黑箱一些输入,就会得到一些输出,而不必理会黑箱内部的运行细节。
黑箱的输入和输出:
\qquad
对于函数的设计者,最重要的工作是封装,封装意味着对外提供服务并隐藏细节。对于一个封装良好的函数而言,其对外提供服务的接口应当是简洁的,内部功能应当是明确的。
二、函数的定义
- 函数头:函数对外的公开接口
a.函数名称:命名规则与跟变量一致,一般取与函数实际功能相符合的、顾名思义的名称。
b.参数列表:即黑箱的输入数据列表,一个函数可有一个或多个参数,也可以不需要参数。
c.返回类型:即黑箱的输出数据类型,一个函数可不返回数据,但最多只能返回一个数据。 - 函数体:函数功能的内部实现
- 语法说明:
返回类型 函数名称(参数1, 参数2, ……) // 函数头
{
// 函数体
}
- 函数示例1:求两个给定的整数的最大值
int max(int x, int y) // 该函数接收两个整型参数,并返回一个整型数据
{
int z;
z = x>y ? x : y;
return z;
}
- 函数示例2:交换两个浮点数
void swap(double *p1, double *p2) // 该函数接收两个浮点指针参数,不返回数据
{
if(p1 == NULL || p2 == NULL)
return;
double tmp;
tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
- 函数示例3:初始化液晶屏,获取显存入口指针
char * initLCD(void) // 该函数不接受参数,返回一个字符指针
{
int lcd = open("/dev/fb0", O_RDWR);
struct fb_var_screeninfo vinfo;
ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo);
int bpp = vinfo.bits_per_pixel;
int size = vinfo.xres * vinfo.yres * bpp/8;
char * fbmem = mmap(NULL, size, PROT_READ, MAP_SHARED, lcd, 0);
return fbmem;
}
- 语法汇总:
- a.当函数的参数列表为 void 时,表示该函数不需要任何参数。
- b.当函数的返回类型为 void 时,表示该函数不返回任何数据。
- c.关键字 return 表示退出函数。
①若函数头中规定有返回数据类型,则 return 需携带一个类型与之匹配的数据;
②若函数头中规定返回类型为 void,则 return 不需携带参数。
三、实参与形参
- 概念:
- 函数调用中的参数,被称为实参,即 arguments
- 函数定义中的参数,被称为形参,即 parameters
- 实参与形参的关系:
- 实参与形参的类型和个数必须一一对应。
- 形参的值由实参初始化。
- 形参与实参位于不同的内存区域,彼此独立。
- 示例:
// 函数定义中,x、y都属于形参,位于函数 max 的栈内存中
// 它们的值由实参一一对应初始化
int max(int x, int y)
{
int z;
z = x>y ? x : y;
return z;
}
int main(void)
{
int a = 1;
int b = 2;
int m;
// 函数调用中,a、b都属于实参,存储于主函数 main 的栈内存中
m = max(a, b);
}
四、函数调用的流程
\qquad
函数调用时,进程的上下文会切换到被调函数,当被调函数执行完毕之后再切换回去。
函数调用时代码的执行流程
五、局部变量与栈内存
- 局部变量概念:凡是被一对花括号包含的变量,称为局部变量
- 局部变量特点:
- 某一函数内部的局部变量,存储在该函数特定的栈内存中
- 局部变量只能在该函数内可见,在该函数外部不可见
- 当该函数退出后,局部变量所占的内存立即被系统回收,因此局部变量也称为临时变量
- 函数的形参虽然不被花括号所包含,但依然属于该函数的局部变量
- 栈内存特点:
- 每当一个函数被调用时,系统将自动分配一段栈内存给该函数,用于存放其局部变量
- 每当一个函数退出时,系统将自动回收其栈内存
- 系统为函数分配栈内存时,遵循从
上(高地址)往下(低地址)分配
的原则
- 示例代码:
int max(int x, int y) // 变量 x 和 y 存储在max()函数的栈中
{
int z; // 变量 z 存储在max()函数的栈中
z = x>y ? x : y;
return z; // 函数退出后,栈中的x、y 和 z 被系统回收
}
int main(void)
{
int a = 1; // 变量 a 存储在main()函数的栈中
int b = 2; // 变量 b 存储在main()函数的栈中
int m; // 变量 m 存储在main()函数的栈中,未赋值因此其值为随机值
m = max(a, b);
}
函数调用时代码的执行流程:
- 技术要点:
- 栈内存相对而言是比较小的,不适合用来分配尺寸太大的变量。
- return 之后不可再访问函数的局部变量,因此返回一个局部变量的地址通常是错误的。