文章目录
第四章 函数(function)
函数可以把重复的过程精简,减少代码量。可以粗略的理解为将某一项功能打包起来。
4.1 结构化程序设计
一、结构化程序设计原则
一句话:高内聚,低耦合
关于内聚: 更具专业的来讲内聚,应该是说功能内聚,具有相同功能的接口内聚,比如,在实际开发中,项目组后端开发,有的开发A模块,有的开发B模块,而开发A,B模块都不会涉及到前台技术,开发A,B模块的人就可以理解为内聚的,而前端组开发前台页面,思考如何展示数据,后台处理数据,以及数据入库CRUD,那么此时前后台人员分工合作,中间通过暴露接口通信,那么耦合结合着对于类似的前端和后台来说, 前后端分离就是为了降低耦合度
比如一个商城系统,其中订单系统和库存系统来说,二者存在的关系是订单如果增加,库存会随之减少,但二者如果放到一个模块中,除了开发不规范之外,二者的功能中会出现较高的耦合度,所以二者就需要通过暴露接口,来处理彼此之间的数据转换,这样就是降低耦合,即低耦合,那么在开发中,订单模块涵盖的功能集中针对订单的处理,功能协作度高,那么这个就是所谓的内聚了,即聚拢相关度高的功能,功能具有针对性.
更多内容请参看「低耦合,高内聚」真的好吗?
二、模块与函数
函数体现结构化的设计思想
三、c源程序结构
- 组成:
一个main()
,加上n个其他function - 函数定义不可以嵌套,调用可嵌套,函数也可以互相调用,但不能调用
main()
- 函数的分类:
- 用户角度
- 系统函数
- 用户自定义函数
- 参数角度
- 有参函数
- 无参函数
- 用户角度
4.2 函数的定义
把一段计算定义成函数,给以命名,定义后就可以在任何需要的地方调用
<stdio.h>
就是封装好的函数库,在代码里include
,后面就可以使用printf,scanf等函数
。
这个例子并不严谨,仅帮助理解。
一、定义格式
一个函数包含函数头和函数体,具体细节如下
return_type function_name( parameter list )//第一行称为函数头
{
body of the function //函数体,通常需要返回数据
return (value)
}
二、格式说明
- 函数头包括
return_type
返回值function_name
函数名perameter list
参数表- 函数可以有0个或多个参数,称为形式参数。形式参数之间用
,
连接 - 每个参数必须指明参数类型和参数名称
- 形式参数为局部变量1
- 形参的值由调用者传入(参数传递)
- 函数可以有0个或多个参数,称为形式参数。形式参数之间用
- 函数体
实现功能的计算代码,通常要有return
你不给也不能把你怎么样(雾)
注意
在定义函数时,需要分别定义形参的类型
int funtion1 (int a,int b)
而
int funtion1 (int a, b)是错误的。
例子
#include<stdio.h>
#include<math.h>
int area (int a,int b)//求面积函数
{
int s;
s=a*b;
return s;//或直接写成 return s=a*b;
}
int sushu (int a)//判断一个数是否是素数,是返回1,否则返回0
{
int k,i;
k=sqrt(a);
for(i=2;i<=k;i++)
{
if(a%i==0)
break;
}
if(i>k)
return 1;
else
return 0;
}
int jieceng(long a)//计算阶乘
{
long i=1,s=1;
for(i;i<=a;i++)
{
s=s*i;
}
return s;
}
main()
{
printf("%d",sushu(11));//函数调用
printf("%d",jieceng(5));
}
三、函数返回值return
- 格式
return 表达式
或return(表达式)
- 返回值数量
- 返回值最多一个
- 可以有多条
return
,但只会执行一条
- 函数返回值与函数返回值类型的关联
- 若函数有
return
,则必有返回值的类型,并且类型相同 - 若函数有
return
,但没有规定返回值类型,系统自动为int
型 - 若函数没有
return
,必须写void
- 若函数有
结论:return_type
的优先级大于return(表达式)
4.3 函数的调用
一、函数调用的格式
- 格式:函数名(实参列表)
- 执行流程:
- 说明
- 主调函数:
main()
- 被调函数:用户自定义函数
- 主调函数:
二、函数调用方法
- 函数有返回值:表达式调用
- 函数无返回值:语句调用
例:语句调用
void prt1()
{ printf("**************\n"); }
void prt2()
{ printf("I'm happy\n"); }
main()
{ prt1();prt2();prt1(); }
结果:
三、函数调用的特殊说明–函数原型
先定义,后使用
- 格式:
return_type function_name( parameter list );
parameter list
可以只写形参类型
例:
int jieceng(long);
main()
{
printf("%d",jieceng(9));
}
int jieceng(long a)
{
long i=1,s=1;
for(i;i<=a;i++)
s=s*i;
return s;
}
- 特殊情况
- 当函数定义在上,调用在下可以不写
- 返回值类型为int,可以不写
四、参数传递
- 概念
- 形参:被调用函数中的变量
- 实参:
- 参数传递:实参向形参进行单向值传递。形参的改变不影响实参
- 参数传递机制:
1. 内存中:形参被调时,分配空间,调用结束时,释放空间
2. 名字上:形参与实参可以同名。
3. 形参可为变量;实参可为变量或常量。
4. 三个一致:个数,类型,顺序。
5. 在自定义函数中- 输入集<===>形参列表
- 输出集<===>
return
例
void swap (int a, int b)
{
int t;
t=a; a=b; b=t;
}
int main()
{
int a=5, b=3;
printf("before swap a=%d, b=%d",a, b);
swap(a,b);
printf("after swap a=%d, b=%d",a, b);
}
两次结果均为
5,3
4.4 变量的作用域与存储属性
一、变量的作用域
变量的作用域即变量的作用范围。
根据变量的位置,产生了局部变量与全局变量。
- 局部变量(Local Variable)
- 定义位置:函数体内
- 作用域:定义函数可用
- 说明
- 复合语句中的局部变量只能在符合语句中可用
- 主函数不特殊(该咋地咋地)
- 不同函数中,局部变量可以同名
- 全局变量(Global Variable)
- 定义位置:函数体外。
- 作用域:定义的位置到文件尾部。
- 说明
extern
可扩展全局变量作用域
- 同名问题:全局变量和局部变量同时出现时,局部变量优先。
例
#include<stdio.h>
int a = 5;
void fun (int b)
{
int a = 10;
a += b; //此a为局部变量
printf("%d\n", a);
}
main()
{
int c = 20;
fun(c);
a += c; //此a全局变量的a,更改的是全局变量。
printf("%d\n", a);
}
二、变量的存储属性
- 以静态形式存储的有:静态变量,全局变量
- 以动态形式存储的有:形参,自动变量,函数调用的堆栈与返回地址时。
详情参看链接:动态存储区、静态存储区、堆和栈的区别
共有auto, static, register, extern
四种存储类型。
- 局部变量存储属性
auto
:自动局部变量,在动态区开辟内存,auto
可省略。
例:auto int a
=int a
register
:寄存器变量,存储在CPU中的寄存器中。1
说明:- 数量有限
- 只能
register int a;
或register char a;
static
:静态局部变量,在静态区开辟内存。
说明:static
定义的局部变量未经初始化,初值为0.- 只在第一次初始化。
- 只在函数内部使用,整个运行周期空间不释放。
- 全局变量存储属性
可放static, extern
静态局部变量static的举例:
#include<stdio.h>
int fun(int x)
{
static int y;
int z = 1;
z += 2;
y += x + z;
return y;
}
main()
{
int k;
for(k=1; k<=3; k++)
{
printf("%3d",fun(k));
}
}
图一为第一次fun
执行完第二次开始时各个变量的值
图二为第二次fun
执行完第三次开始时各个变量的值
总结:
存储类型 | 作用域 | 初始化 | 生命周期 |
---|---|---|---|
auto | 局部变量 | 随机数 | 短 |
register | 局部变量 | 随机数 | 短 |
static | 局部变量,全局变量 | 0 | 长(连续性) |
extern | 全局变量 | 0 | 长 |