C语言函数

函数

形参与实参的关系:

​ 实参和形参位于不同的内存区域,彼此独立

​ 形参变量只有在函数被调用的时候,才会分配内存,在函数调用结束的时候,形参就会自动释放,所有的形参在函数体内有效的,不能在函数体外使用

​ 实参可以是变量、常量、表达式、函数调用,但是,如果是函数调用,要求在函数调用结束的时候,要有确定的值,以便于将这些值传递给函数做形参初始化, 实现给形参赋值。

int max(int x, int y)	//函数定义, x,y叫做形参
{
	return (x>y)?x:y;
}

int main(void)
{
	int x, y;
	scanf("%d%d", &x, &y);
	int m = max(x, y);	//调用函数,x,y叫做实参
//   int m = max(3,8);
//   int m = max(2, 4+8);
//   int m = max(x,func());//func函数要有确定的值
	printf("m = %d\n", m);

	return 0;
}

​ 实参和形参在数量上,类型上,顺序上必须要保持一致

​ 函数调用的时候,数据传输是单向的,不可逆

​ 函数调用的时候,多个形参在做初始化的时候,是按照从右往左的顺序初始化

函数调用的内存逻辑

​ 函数调用时,进程的上下文会切换到被调函数,当被调函数执行完之后,在切换回去

int main(void)
{
	int x, y;
	scanf("%d%d", &x, &y);
	int m = max(x, y);	//调用函数,x,y叫做实参
	printf("m = %d\n", m);
	return 0;
}

int max(int x, int y)//函数定义, x,y叫做形参
{	
	return (x>y)?x:y;
}

在这里插入图片描述

局部变量与栈内存

局部变量:定义在函数体内的变量(函数的形参也属于局部变量),就叫做局部变量

全局变量:定义在函数体外部的变量,全局变量在程序退出之前都是有效的。

​ 局部概念:只要是通过大括号括起来的变量,也称为局部变量

{
   	int b = 2;
}
   printf("b = %d\n", b);
这里不能打印b的值,b是属于上一个大括号里面的局部变量,出了这个大括号之后,b就被释放掉,变成无效的

局部变量的特点:

​ 某一函数内部的局部变量,存储在该函数的特定的栈内存中

​ 局部变量只能在该函数内可见,在该函数外部不可见

​ 当该函数退出后,局部变量所占的内存立即被系统回收,因此局部变量也被称为临时变量

​ 函数的形参虽然不被花括号包含,但依然属于该函数的局部变量

栈内存的特点:

​ 每当一个函数被调用时,系统将自动分配一段栈内存给该函数,用于存放其局部变量

​ 每当一个函数退出时,系统将自动回收其栈内存

​ 系统为函数分配栈内存的时候,是遵循从上(高地址)往下(低地址)的分配原则

技术要点:

栈内存相对而言比较小的,不适合用来分配尺寸太大的变量

​ return之后不可再访问函数的局部变量,因此返回一个局部变量的地址通常是错误的

static关键字和静态函数

静态变量:

​ 用static关键字修饰的变量就叫做静态变量,比如 static int a;

​ static关键字修饰局部变量,这个变量从定义位置开始被分配到数据段里面,直接程序退出才会释放空间,并且这个变量只会初始化一次。

​ static关键字修饰全局变量,这个全局变量只在当前文件有效

static int m;	//让全局变量只在当前文件有效
int m;			//让全局变量被其他文件做外部声明

void func(void)
{
	static int a = 0;  //a只会被初始化一次,程序退出a才会被释放
	printf("a = %d\n", a++);
	return ;
}

int main(int argc, char const *argv[])
{
	func();//a = 0
	func();//a = 1
	func();//a = 2

	return 0;
}

静态函数:

​ 背景知识:普通函数都是跨文件使用的,即在a.c里面写的函数可以在b.c里面使用

​ 静态函数:用static修饰的函数,只能在定义的文件内可见的函数,称为静态函数

static void d_func(void)
{
	printf("这是个外部函数\n");
}

要点:

​ 静态函数主要是为了缩小函数的可见范围,减少与其他文件中重名函数冲突的概率

​ 静态函数一般定义在头文件中,然后被各个源文件包含

使用递归的条件

​ 子问题的逻辑与原问题一样,并且更为简单

​ 不能无限制重复调用本身,必须有一个出口,化简为非递归状态出来

设计一个递归算法

​ 确定问题变量

​ 确定递归公式(化简过程,比如n-1)

​ 确定边界条件

注意:递归是一个函数嵌套调用的过程,递归次数越多,运行时间会大幅度增加,内存空间也会大量占用,所以,一般能用循环解决的,我们就不考虑递归
缺点:效率低还比较占内存
递归算法的一般应用

​ 数据的结构形式按照递归定义的(比如:二叉树)

​ 执行过程按照递归定义的(比如:函数的嵌套调用)

​ 问题的解决办法按照递归算法实现(比如:年龄问题,汉诺塔)

递归调用时,函数的栈内存会随着层层深入,栈空间就往下增长,如果递归的层次太深,很容易将栈内存耗光。

层层递进时,问题的规模会随之减小,减小到可直接退出的条件时,函数开始层层回归

内联函数使用原因分析:
函数切换过程中,保护现场和恢复现场等工作具有一定的时间消耗,这个时间大约是一两条语句的执行时间,假如被调用函数本身很简短,只包含一两条语句,函数切换和保护现场和恢复现场与函数本身的执行时间在同一个数量级,那么这种切换的时间不能忽略
这样就可以将这个函数声明为内联函数

inline关键字

内联函数的语法
inline void func(void)
{
printf(“hello\n”);
}
inline这个关键字在C99标准之前是没有这个关键字的,有的编译器是编译不了这个关键字的,比如16版本Ubuntu的gcc,需要找一个能够编译这个关键字的编译器,比如16版本Ubuntu里面的g++

什么时候用到内联?
该函数的代码简短,简短到跟函数切换所用到的时间具有可比性
该函数被频繁调用
当满足上面这两个条件的时候,就可以考虑使用内联。实际上在一些大型项目中,用到内联优化的代码只是热点代码

回调函数

概念:回调函数就是一个通过函数指针调用的函数

char *chushi(char *cy, char *jl);//回调函数

char *xiaomei(int m, char *cy, char *jl)
{
printf(“买菜\n”);
return chushi(cy, jl);
}

char *chushi(char *cy, char *jl)//回调函数
{
return “烤鱼”;
}

int main(int argc, char const *argv[])//我
{
printf(“%s\n”, xiaomei(20, “烤鱼”, “加辣”));

return 0;

}

main函数的传参和返回

main函数的返回
普通函数返回时到这个函数调用者,main函数的返回值返回给main函数调用者,执行这个程序的位置,直接程序退出

main函数的传参
执行该程序的父进程,调用main函数所在程序的父进程给main函数传递的数据,这些是可以被main函数形参接收
给main函数传参的格式

传参:main(int argc, char const *argv[])
main(int argc, char const **argv)
怎么传:
./可执行文件名 参数1 参数2 参数3 。。。。。。
./main 2356 8545 26489

不传参:main() main(void)

参数含义:
argc:传入参数个数
argv:每个参数的内容(argv是一个char型的指针数组)

注意:main函数传入的参数都是以字符串保存的,那么我们如果要传入的数据是数值类型,那对于main函数来讲,是以字符串处理的,我们可以通过atoi函数来将数字字符串转换成整数

#include <stdlib.h>

int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yengi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值