- 在C语言中,函数至关重要,函数意味着功能模块,一个典型的C语言程序就是由一个个的功能模块拼接而成的整体。也因为如此,C语言被称为模块化语言。
-
函数的概念
- 对于函数的使用者,可以将函数理解为黑箱子,使用者只管按照规定给黑箱一些输入,就会得到一些输出,而不必理会黑箱子里面的运行细节。
-
函数的定义
函数由函数头和函数体两部分组成。
函数头:相当于函数对外的接口
语法说明
返回值类型 函数名称(输入参数列表) { C语句,函数功能的具体的实现(函数体) ...... } 1.函数名称:命名规则与变量一致,一般取与函数功能相符合的、顾名思义的名称。2.参数列表:即黑箱的输入数据列表,一个函数可有一个或多个参数,也可以不需要参数。
3. 返回类型:即黑箱的输入数据类型,一个函数可不返回数据,但最多只能返回一个数据
函数体:函数功能的内部实现
- 示例
/*
max : 求给定两个整数的最大值
@ a
@ b
返回值:
最大值
*/
int max(int a, int b)
{
return a > b ? a : b;
}
- 语法汇总:
-
函数调用
语法说明
函数名(实参列表)//实参:传给函数的值
以取两个数的最大值为例:
#include <stdio.h>
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int max_val = max(3, 5);
printf("最大值是:%d\n", max_val);
return 0;
}
-
行参与实参
实参:函数调用时传给被调函数的参数,如上述例子中max(3,5),3,5就是实参。
形参:用于接受调用函数所传数据的参数,如上述例子中max(int a,int b),a,b就是形参。
- note
- 形参与实参的类型和个数必须一一对应。
- 形参的值由实参初始换。
- 形参与实参位于不同的内存空间,彼此独立。
-
内存分布
每个C语言进程都拥有一片结构相同的虚拟内存,所谓的虚拟内存,就是从实际物理内存映射出来的地址规范范围,最重要的特征是所有的虚拟内存布局都是相同的,极大的方便内核管理不同的进程。而每个虚拟内存都拥有以下四个区域:
-
栈(stack)
-
堆(heap)
-
数据段 .rodata .data .bss
-
代码段: .init .text
-
栈内存
存在于栈内存的有:环境变量,命令行参数,局部变量。
处于栈内存里的变量,空间随函数的释放而释放,栈内存的分配和释放都是由系统规定的,我们无法干预。
-
静态数据
c语言中,静态数据有两种:
-
全局变量:定义在函数外的变量
-
静态局部变量:定义在函数内部,且被static修饰的变量
-
note:
-
若定义时未初始化,则系统会将所有的静态数据自动初始化为0
-
静态数据初始化语句,只会执行一遍。
-
静态数据从程序开始运行时便已存在,直到程序退出时才释放
-
数据段和代码段
-
数据段细分成如下几个区域:
-
.bss 段:存放未初始化的静态数据与全局变量,它们将被系统自动初始化为0
-
.data段:存放已初始化的static静态数据与全局变量
-
.rodata段:存放常量数据
-
-
代码段细分成如下几个区域:
-
.text段:存放用户代码
-
.init段:存放系统初始化代码
-
数据段和代码段内存的分配和释放,都是由系统规定的,我们无法干预。
-
堆内存
-
堆内存基本特征:
-
相比栈内存,堆的总大小仅受限于物理内存,在物理内存允许的范围内,系统对堆内存的申请不做限制。
-
相比栈内存,堆内存从下往上增长。
-
堆内存是匿名的,只能由指针来访问。
-
自定义分配的堆内存,除非开发者主动释放,否则永不释放,直到程序退出。
-
-
相关函数:
-
申请堆内存:malloc() / calloc()
-
清零堆内存:bzero()
-
释放堆内存:free()
-
-
函数指针与指针函数数组
概念:
函数指针变量就是用来保存函数地址的变量。
int func(int a, int b);
// 函数指针,指向的函数类型为返回值为int 参数为(int,int)的函数
int (*pfunc)(int a, int b);
给函数改别名:
以上用来保存函数地址的变量看起来非常冗余复杂,采用改别名的方式可以增强代码的易读性。
eg:
// 此时fptr就相当于void (*fptr)(int *a, int *b)的类型
typedef void (*fptr)(int *a, int *b);// 类似于 int类型 int a = 10
// 函数指针数组,数组存放的是指向返回值为 int 参数为int int类型的函数地址
int (*pfbuf[]) (int,int);
相关习题:
typedef 定义函数指针数组,初始化 sub(减法) div(除法) rem(取余数)
#include <stdio.h>
typedef int (*fpbuf[3])(int, int);
int sub(int a, int b)
{
return a - b;
}
int div(int a, int b)
{
return a / b;
}
int rem(int a, int b)
{
return a % b;
}
int main(int argc, char const *argv[])
{
int(*pbuf[3])(int ,int) = {sub, div, rem};
int a, b;
fpbuf fbuf={sub, div, rem};
printf("请输入:");
scanf("%d%d", &a, &b);
printf("sub=%d\n", pbuf[0](a, b));
printf("div=%d\n", fbuf[1](a, b));
printf("rem=%d\n", pbuf[2](a, b));
return 0;
}
-
回调函数(钩子函数)
调用一个函数,这个函数传递函数指针,被调用的的这个函数通过这个函数指针进行调用其他函数,我们把这种方式称为回调
eg:
将两数相加设计成回调函数:
callback.c:
#include <stdio.h>
#include "callback.h"
int callback(p_callback_add func)
{
int a = 10, b = 20;
return func(10, 20);
}
callback.h:
typedef int ( (*p_callback_add)(int,int));//改别名
extern int callback(p_callback_add func);
main.c:
#include <stdio.h>
#include "callback.h"
int add(int a,int b)
{
return a+b;
}
int main(int argc, char const *argv[])
{
int ret=callback(add);
printf("ret=%d\n",ret);
return 0;
}
-
内联函数
内联函数的核心在于用空间换时间,当多次调用同一个函数时,很耗时间,这时就可将被调用的函数设置为内联函数以节省调用时间,加快运行效率。
inline注意事项
-
内联函数在头文件实现,其它函数不要在头文件实现
-
函数声明和函数实现都需要添加关键字inline,如果函数声明没有添加extern 和 inline 关键字,会报错
demo:
通过内联函数实现获取两个数的最小值:
main.h:
int min(int a, int b);
inline int min(int a, int b)
{
return a < b ? a : b;
}
main.c:
#include <stdio.h>
#include "main.h"
int main()
{
int a=27,b=23;
printf("min=%d\n",min(a,b));
printf("min=%d\n",min(a,b));
printf("min=%d\n",min(a,b));
printf("min=%d\n",min(a,b));
return 0;
}
-
递归函数
概念:如果一个函数内部,包含了对自身的调用,则该函数称为递归函数。
note:
递归函数注意要有明显的结束条件,不然函数会进入无限递归,结束不了。
习题:
幂运算问题:
首先可以写一个非递归实现幂运算:
#include <stdio.h>
int main()
{
int x, n;
int ret = 1;
int count = 0;//用以计数与幂指数比较
printf("请输入:");
scanf("%d%d", &x, &n);
if (n == 0)
{
ret = 1;
}
else
{
while (1)
{
ret *= x;
count++;
if (count == n)
break;
}
}
printf("ret=%d\n", ret);
return 0;
}
递归实现幂运算:
#include <stdio.h>
float Pow(int x, int n)
{
if (x == 1 || n == 0)//结束条件
return 1;
else if (n == 1 || x == 1)//特殊情况
return x;
else
return x * Pow(x, n - 1);
}
int main()
{
int x, n;
float ret;
printf("请输入:");
scanf("%d%d", &x, &n);
if (n < 0)
{
ret = 1 / Pow(x, -n);
}
else
ret = Pow(x, n);
printf("ret=%.4f\n", ret);
return 0;
}
通过对比递归比非递归,可以发现,用递归实现幂运算更为方便。
标签:return,函数,int,菜鸟,ret,内存,printf,日记 From: https://blog.csdn.net/qq_65363200/article/details/140394427