初识C语言(函数)

一、什么是函数

函数是一个个代码的组合,相当于是个代码块,可以完成程序员自主编写的功能

二、函数的种类

函数主要分为两类:自定义函数和库函数

1.库函数

库函数的存在是因为某一些功能会被程序员重复使用,如计算字符串的长度,计算变量的大小等,为了程序的高效和C语言的可移植性,所以C语言提供了这样一个标准库,只需要引用对应的头文件,就可以使用了

库函数的使用:需要包含这个库函数的头文件

#include <stdio.h>    //这个是printf函数的头文件

2.自定义函数

但是库函数没有办法全部描述生活中的问题,或者说实现我们想要达到的效果,这时候就需要程序员自己自定义函数,来完成指定的功能

三、函数的形式(包括一些使用)

eg.double sqrt(double x);
返回类型 函数名 参数

函数是由上面三个部分组成的,下面我将会给大家一一介绍

1.返回类型

1.1返回类型是和返回值一一对应的

int get_max(int x, int y)
{
	if (x > y)
		return x;
	else
		return y;   //返回的x和y一定都是int类型的
}

1.2 如果我不想有返回值该怎么办

用void,这个函数就不会有返回值,相当于是函数把下面要执行的代码给包裹起来了

void menu(void)
{
	printf("hahaha");
}
int main()
{
	menu();      //单纯打印出hahaha,不要给我返回什么值
	return 0;
}

1.3如果我没有写返回类型会发生什么

在这里插入图片描述
如果没有设置返回类型,编译器会默认设置为int类型

2.函数名

2.1 函数/变量的取名要有意义

  • 不要用a,b,c之类的,没有什么特殊含义的来取名
  • 不要用拼音或者中英混杂,会显得很low
  • 命名不要太过
    • is_leap_year 可以-----字符+下划线
    • IsLeapYear 可以-----首字母大写
    • Is_Leap_Year 不可以----命名得太过了

3.函数参数

函数的参数必须要求是一个确定的值,可以是变量、表达式、函数等

3.1 实参VS形参

含义

  • 实参是在调用这个函数的时候传过去的值(真实传给函数的参数),可以是:常量、变量、表达式、函数等,要求这是个确定的值,以便这个数可以传送给形参
    实参最好不要是全局变量:难以控制而且会降低代码的质量

  • 形参是定义函数的时候,用来接收实参的。形式上,就是函数名后括号里的变量,在调用的时候才会分配内容,出了这个函数就会被销毁—只在函数内部有效

函数内部实现逻辑(以3.2的代码为例)
main函数里面调用了Add函数,调用时传进去的a和b就是实参,而上面Add函数里面所定义的x和y就是对应接收实参a和b的形参。
a,b会在main函数这个栈帧区间最后所压上的edi、eai、ebx上面创建空间来存放传过去的值。
而x和y是不会多余创造空间的,它们直接是去访问存放存放值的那个地址,也就是说形参只是实参的临时拷贝,形参的改变不会影响实参,这也是下面要介绍的传参调用和传址调用的主要区分点

调用函数里面的参数是可以名字一样的,因为调用函数彼此之间相互独立,是不会去访问对方的

3.2 传值调用

#include <stdio.h>
//计算3+5的值
void Add(int x, int y)
{
	printf("%d", x + y);
}
int main()
{
	int a = 3;
	int b = 5;
	Add(a, b);
	return 0;
}

3.3 传址调用

把地址传过去,这样函数里面的实参,就可以改变我实参传过去的值

#include <stdio.h>
//实现交换
void Add(int* x, int* y)
{
	*x = 5;
	*y = 3;
	printf("%d %d\n", *x,*y);
}
int main()
{
	int a = 3;
	int b = 5;
	printf("%d %d\n", a, b);
	Add(&a, &b);
	return 0;
}

3.4 如何选择是传参还是传址

如果这个函数可能会改变我穿过去的形参的值,那我就要采用传址调用,使得形参的改变可以影响我的实参
如3.3中,我穿过去的a是3,b是5,但是函数要实现的是把我的a改成5,b改成3,所以就要采用传址调用;而3.2中,只是单纯把a和b相加,本身是没有改变a和b的值的,所以可以采用传值调用

4.返回多个值

4.1通过数组返回

#include <stdio.h>
void test(int arr[])
{
    arr[0] = 3;
    arr[1] = 4;
}
int main()
{
    int arr[2] = { 0 }; 
    printf("%d %d\n", arr[0], arr[1]);
    test(arr);
    printf("%d %d\n", arr[0], arr[1]);

    return 0;
}

4.2 通过全局变量返回

int x = 0;
int y = 0;
void test()
{
    x = 3;
    y = 4;
}
int main()
{
    test();
    printf("%d %d\n", x, y);
    return 0;
}

四、函数的使用

函数的使用需要声明和定义

1.声明

void Add(int* x, int* y)

告诉程序有这个函数,这个函数的返回类型是什么,参数是什么,函数名是什么,但是它是如何实现的,是否真的有这个函数,都算是未知数。

就相当于排队时,给人占一个位,主观上这里等会会来一个人,但是实际上这个人最终会不会在队排完前来,依旧是一个未知数

注意声明放的位置:

  • 声明要放在调用函数的代码前面,因为程序是一条一条执行下来的,如果声明在使用前,程序即使后面知道你声明了,也会报错。
  • 一般要放在头文件里面
int Add(int x,int y);
int Add(int ,int );    //只是告诉程序有没有这个函数,传值也计算不了
#define M 100

2.定义

说明这个函数是如何实现的,这个函数里面的代码是什么样的,一般放在源文件里面

void Add(int* x, int* y)
{
	*x = 5;
	*y = 3;
	printf("%d %d\n", *x,*y);
}

3.声明和定义的关系

定义是一种特殊的声明,事实上,如果我已经定义了是没有什么必要再去声明的

4.extern

如果主函数和调用的函数在不同的源文件,我们可以通过extern进行声明

extern void Add(int* x, int* y)

5.额外补充

我们在写项目的时候,绝不是把声明和定义就这样,全部放在主函数的那个源文件上。

我们会自己写头文件和对应的源文件,主函数那边只要把自定义的头文件包含就可以了

#include "add.h"    //自定义的头文件用双引号

函数可以嵌套调用(函数里面调用函数),但是不能嵌套定义

可以把函数的返回值作为另一个函数的参数,这个操作叫“链式访问”

5.递归和迭代

5.1 递归

#include <stdio.h>
int is_my_strlen(char* ch)
{
	if (*ch != '\0')
	{
		return 1 + is_my_strlen(ch + 1);
	}
	else
	{
		return 0;
	}
}
int main()
{
	char ch[] = "bit";
	int ret = is_my_strlen(ch);
	printf("%d", ret);
	return 0;
}
5.1.1 定义

递归就是在这个函数里面调用这个函数(自己调用自己)

为了方便理解,可以不要把一个递归函数中调用自己的函数看作是在调用自己,而就当它是在调另一个函数。只不过,这个函数和自己长得一样而已。

思考方式
(1)把一个复杂问题拆成几个相似的小问题,即大事化小,小事化了

(2)理解什么时候递,什么时候归

5.1.2 递归的条件
  • 存在限制条件,满足这个限制条件后,递归就不再继续
  • 每次递归调用后越来越接近这个限制条件

5.2 迭代

迭代就是“不是递归的情况”,效率高,但是代码可读性不大

#include <stdio.h>
void is_my_strlen(char* ch)
{
	int count = 0;
	while (*ch != '\0')
	{
		count++;
		ch ++;
	}
	printf("%d", count);       //指针的进位
}
int main()
{
	char ch[] = "bit";
	is_my_strlen(ch);

	return 0;
}

5.3 递归和迭代的使用场景

递归形式清晰,但是会有栈溢出和运行效率低的问题,如果在写某个代码时,这个问题不是很明显,就用递归,否则就用迭代,或者迭代里面的变量用static修饰,转为静态变量

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值