一、函数定义

子程序,负责完成某项特定任务,相较于其他代码,具备相对的独立性


二、函数的分类

1.库函数
2.自定义函数


2.1库函数

c语言本身提供的一种函数
常用:   

      IO函数:输入输出相关函数,头文件为<stdio.h>
      字符串操作函数 : <string.h>
      字符操作函数
      内存操作函数
      时间或日期函数
      数学函数
      其他库函数


以下两个例子都需要头文件<string.h>,但在VS2022比较智能,没有错误和警告

​​​​例1:strcpy 由名字联想->字符串拷贝
 

#include <stdio.h>
int main()
{
	char arr1[20] = { 0 };     //char为字符型
	char arr2[] = "hello bit"; //int arr2[] = { hello bit };字符串用""
	strcpy(arr1, arr2);//前为destination,后为source
	printf("%s", arr1);//字符串打印用%s
	return 0;
}

例2memset->内存设置

//将hello改为xxxxx
#include<stdio.h>
int main()
{
	char arr[20] = "hello word";
	memset(arr,'x',5);
	printf("%s", arr);
	return 0;
}
将wor改为yyy
#include<stdio.h>
int main()
{
	char arr[20] = "hello world";//arr在h前 arr+1在e前
	memset(arr+6, 'y', 3);//arr+6在w前
	printf("%s\n", arr);
	return 0;
}

如何学会使用库函数?:
     http://en.cppreference.com
     http://zn.cppreference.com
     尽量使用英文版去看

2.2自定义函数

//函数的定义
int MAX(int x, int y)//函数类型与返回值类型相同
{
	return(x > y ? x : y);//三目运算符
}
#define _CRT_SECURE_NO_WARNINGS//放在最上边才能识别
#include<stdio.h>
int main()
{
	int a = 0;
	int b = 0;
	//函数的调用
	scanf("%d%d", &a, &b);//scanf中""里是什么样的格式就输入成什么样的格式,但像这种"%d%d"两个整数要输入空格
	int m = MAX(a, b);
	printf("%d", m);
	return 0;
}
//错误写法
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void swap(int x, int y)//x,y叫形参
{
	int z = 0;         //x,y,z,a,b都有自己的地址,此函数只交换了x,y的地址,不影响a和b
	z = x;
	x = y;
	y = z;
}
//给面试官的解释
//当时实参传递给形参时,形参是实参的一份临时拷贝,此时形参占独立的内存
//对形参的修改不会影响实参
int main()
{
	int a = 0;
	int b = 0;
	
	scanf("%d%d", &a, &b);//a和b是实参
	printf("交换前:a=%d,b=%d\n",a,b);
	swap(a,b);
    printf("交换后:a=%d,b=%d\n", a, b);
	return 0;
}
//正确写法
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void swap(int *px, int *py)
{
	int z = *px;//a与b的值交换了,地址没变
	*px = *py;//不加*只是交换了地址,加*让地址相应的值交换
	*py = z;
}
int main()
{
	int a = 0;
	int b = 0;

	scanf("%d%d", &a, &b);
	printf("交换前:a=%d,b=%d\n", a, b);
	swap(&a, &b);
	printf("交换后:a=%d,b=%d\n", a, b);
	return 0;
}

三、函数的参数

3.1实际参数

真实传给函数的参数,叫实参
实参可以是:常量,变量,表达式,函数等


例如

int Add(int x, int y)
 {
    int z = 0;
    z = x + y;
    return 0;
}

     int c=Add(a+3,b);
     int c=Add(Add(a,3),b);

//无论实参适合种类型的量,在进行函数调用时,它们都有确定的值,以便把这些值传给形参


3.2形式参数


指函数名后括号里的变量,因为形参只有在函数被调用的过程中才实例化
当函数调用完成之后就自动销毁了,与局部变量类似


四、函数的调用

void swap1(int x, int y)
{
    int z = 0;         
    z = x;
    x = y;
    y = z;
}
void swap2(int *px, int *py)
{
    int z = 0;
    z = *px;
    *px = *py;
    *py = *z;
}

传值调用:
swap1(a, b);
函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参

传址调用:
真正建立联系,函数内部可以直接操作函数外部的变量
swap2(&a, &b);


五、函数的嵌套调用和链式访问

5.1嵌套调用



5.2链式访问前提条件:(函数必须有返回值)

#include<stdio.h>
#include<string.h>
int main()
{
	int len = 0;
	len=strlen("abcdef");
	printf("%d\n", len);
	printf("%d\n", strlen("abcdef")); /*链式访问*//*把一个函数的返回值变成另一个函数的参数*/
	                                  //需要注意strlen的返回值类型
    printf("%d", printf("%d", printf("%d", 43)));//printf的返回值是打印字符的个数
	return 0;
}

扩展

int mian(void)  //明确表明main中无参数,本质上main是有参数的
{
    return 0;
}
int main(int argc,char*argv[],char*envp[])
{
    return 0;
}


六、函数的声明和定义

6.1函数声明

一般放在头文件 源文件加上#include"add.h" 本质上将文件add.h的内容拷贝过来
函数定义在main函数后,会警告
解决方法:需要函数的声明,在main函数前些上 add(int x, int y);



6.2函数定义


七、函数递归

7.1什么是函数的递归


自己调自己
把大事化小


7.2递归的俩个必要条件


存在限制条件,当满足时递归将不再继续
每次递归调用是越来越接近这个条件
 


7.3递归与迭代

求n的阶乘(递归)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int fac(int n)//错误一:未标明类型
{
	if (n == 1)//错误二:写为n=1
	{
		return 1;
	}
	else
	{
		return n * fac(n - 1);
	}
}

int main()
{
	int n=0;
	int ret = 0;
	scanf("%d", &n);
	ret=fac(n);
	printf("%d", ret);
	return 0;
}

求n的阶乘(迭代---非递归)

int fac(int n)
{
	int ret = 1;
	int i = 0;
	for (i = 1; i <= n; i++)
	{
		ret*=i;
	}
	return ret;
}

int main()
{
	int n=0;
	int ret = 0;
	scanf("%d", &n);
	ret=fac(n);
	printf("%d", ret);
	return 0;
}

求第n个斐波那契数列(递归)

int Fib(int n)
{
	if (n <= 2)
	return 1;
	else
	{
		return Fib(n - 1) + Fib(n - 2);
	}
}
int main()
{
	int n = 0;
	int ret = 0;
	scanf("%d", &n);
	ret = Fib(n);
	printf("%d", ret);
	return 0;
}

使用递归有时效率低下,重复率高,此时可以选择函数的迭代

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐