函数的分类

  • 库函数
  • 自定义函数

1.库函数:

定义:C语言本身提供的函数 分类:1. IO函数(输入输出函数) 2.字符串操作函数 3.字符操作函数 4.内存操作函数 5.时间日期操作函数 6.数学函数 7.其他库函数

例:
字符串操作函数
strcpy 拷贝函数
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "bit";
	char arr2[20] = "********";
	strcpy_s(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

上述代码的打印结果为bit

内存操作函数
#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "Hello World"
    memset(arr, '*', 5);
	printf("%s\n", arr);
	return 0;
}

上述代码的打印结果为✳✳✳✳✳ world

2.自定义函数

例:
1.比较两边量的大小
#include<stdio.h>
int get_max(int x, int y)
{
	if (x > y)
		return x;
	else
		return y;
}
int main()
{
	int a = 10;
	int b = 20;
	int max = get_max(a, b);
	printf("%d\n", max);
	return 0;
}

2.利用一个函数交换两变量的值

错误写法:

#include<stdio.h>
void Swap(int x, int y)
{
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	printf("a=%d b=%d\n",a,b);
    Swap(a, b);
	printf("a=%d b=%d\n",a,b);

	return 0;
}

==上述函数代码错误的原因:函数中的x,y与代码中的a,b属于两个空间。不能改变变量的值来达到想要的结果==

正确写法:

#include<stdio.h>
void Swap(int*x, int*y)
{
	int tmp = 0;
	tmp = *x;
	*x = *y;
	*y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	printf("a=%d b=%d\n", a, b);
	Swap(&a, &b);
	printf("a=%d b=%d", a, b);
	return 0;
}

==此代码运用到了指针变量和解引用操作==

函数的参数

  • 实际参数:

真正传给函数的参数。实参可以是常量、变量、函数、表达式。 实参要有确定的值,以便将这些值传给形参。

  • 形式参数:

函数名后括号中的参数,形参只有在函数被调用时才实例化。 形参在调用后会自动销毁,所以形参只有在函数中才有效。

==注意:当实参传给形参时,形参只是对实参的一种临时拷贝。对形参的修改是不会改变实参的==

函数的调用

  • 传值调用

函数的实参和形参站有不同的内存块,对形参的修改不会影响实参

  • 传址调用

是一种将函数外部创建的函数地址传给函数参数的传参方式。 这种传参方式可以将函数和函数外边的变量联系起来,也就是函数内部可以直接操作函数外部的变量。

例:

1.写一个函数可以判断一个数是不是素数
#include<stdio.h>
int is_prime(int n)
{
	int i = 0;
	for (i = 2; i < n; i++)
	{
		if (n % i == 0)
			return 0;
	}
	return 1;
}
int main()
{
	int n = 0;
	scanf_s("%d", &n);
	if (is_prime(n) == 1)
		printf("此数是素数\n");
	else
		printf("此数不是素数\n");
	return 0;
}
2.写一个函数判断一年是不是闰年
#include<stdio.h>
int year(int n)
{
	if (n % 4 ==0&& n % 100 != 0 || n % 400 == 0)
		return 1;
	else
		return 0;
}
int main()
{
	int n = 0;
	scanf_s("%d", &n);
	if (year(n) == 1)
		printf("此年是闰年");
	else
		printf("此年不是闰年");
	return 0;
}
3.写一个函数,实现一个有序数组的二分查找

==本质上arr就是一个指针==

#include<stdio.h>
int binary_search(int arr[], int k,int sz)
{
	int left = 0;
	int right = sz - 1;
	while(left<=right)
	{
		int mid = (left + right) / 2;
		if (arr[mid] < k)
		{
			left = mid + 1;
		}
		else if (arr[mid] > k)
		{
			right = mid - 1;
		}
		else
			return mid;
	}
	return 0;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int k = 7;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = binary_search(arr, k,sz);
	if (i == 0)
		printf("没找到\n");
	else
		printf("找到了,下标为:%d\n", i);
	return 0;
}

==sz要在主函数中被定义,不能再binary_search函数中被定义,因为数组被上面的函数调用时只能调用一个字符,会出现错误。==

4.写一个函数,每调用一次这个函数就会将num的值增加一

需要用到指针

#include<stdio.h>
void Add(int* n)
{
	(*n)++;
}
int main()
{
	int num = 0;
	Add(&num);
	printf("%d\n", num);
	Add(&num);
	printf("%d\n", num);
	Add(&num);
	printf("%d\n", num);
	return 0;
}

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

函数和函数之间是可以有机组合的

嵌套调用

例如:

#include<stdio.h>
void new_line()
{
	printf("hhhh\n");
}
void three_line()
{
	int i = 0;
	for (i = 0; i <= 3; i++)
	{
		new_line();
	}
}
int main()
{
	three_line();
	return 0;
}

链式访问

把一个函数的返回值作为另一个函数的参数

#include<stdio.h>
#include<string.h>
int main()
{
	int len = 0;
	len = strlen("abc");
	printf("%d\n", len);
	return 0;
}

此代码链式访问后为:

#include<stdio.h>
#include<string.h>
int main()
{
	printf("%d\n", strlen("abc"));
	return 0;
}

再如:

#include<stdio.h>
int main()
{
	printf("%d", printf("%d", printf("%d", 43)));
	return 0;
}

此代码打印结果为4321

函数的声明和定义

函数声明

告诉一个编译器一个函数叫什么,参数是什么,返回类型是什么,但具体是不是存在,无关紧要 函数的声明一般放在放在函数使用之前,要先声明后使用 函数的声明一般放在头文件中

函数定义

函数的定义是指函数的具体实现,交代函数的功能实现

test.h的内容放置函数的声明
test.c的内容放置函数的内容

函数的递归

递归的定义:

一个过程或函数在其定义或说明中直接或间接地调用自身的的一种方法。 通常将一个复杂的大问题转化成于其规模相同或相似的一个个小问题来解决,大大减少了程序的代码量。 ==递归的重要思考方式在于:把大事化小==

递归的两个必要条件:

  • 存在限制条件,当满足这个限制条件时,递归便不再继续。

  • 每次递归调用之后会越来越接近于这个限制条

==递归常见错误:栈溢出== 如下:

#include<stdio.h>
int main()
{
	printf("hhh\n");
	main();
	return 0;
}

此代码会循环的打印hhh

#include<stdio.h>
void test(int n)
{
	if (n < 10000)
		test(n + 1);
}
int main()
{
	test(1);
	return 0;
}

此代码会出现栈溢出的情况

练习:

1.接受一个整型值,按照顺序打印出它的每一位。

输入值:1234 输出值:1 2 3 4

#include<stdio.h>
void print(int n)
{
	if (n > 9)
	{
		print(n/10);
	}
	printf("%d ", n % 10);
}
int main()
{
	int num = 0;
	scanf_s("%d", &num);
    print(num);
	return 0;
}
2.求数组中字符串长度

strlen函数

#include<stdio.h>
#include<string.h>
int main()
{
	printf("%d\n", strlen("abc"));
	return 0;
}

模拟调用了一个strlen函数

#include<stdio.h>
int my_strlen(char* str)
{
	int count = 0;
	while (*str != '\0')
	{
		str++;
		count++;
	}
	return count;
}
int main()
{
	char arr[] = "abc";
	int len = my_strlen(&arr);
	printf("%d\n", len);
	return 0;
}

但这种写法在递归中是不允许的。用递归写法如下:

#include<stdio.h>
int my_strlen(char* str)
{
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else
		return 0;
}
int main()
{
	char arr[] = "abc";
	int len = my_strlen(&arr);
	printf("%d\n", len);
	return 0;
}

递归与迭代

#include<stdio.h>
int facial(int n)
{
	int i = 0;
	int ret = 1;
	for (i = 1; i <= n; i++)
	{
		ret = ret * i;
	}
	return ret;
}
int main()
{
	int n = 0;
	int ret = 0;
	scanf_s("%d", &n);
	ret = facial(n);
	printf("%d\n", ret);
	return 0;
}
求第n个斐波那契数(不考虑溢出)

运用递归来求(但此代码计算速度较慢,若想计算速度加快,需要在其中加上循环)

#include<stdio.h>
int Fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return Fib(n - 1) + Fib(n - 2);
}
int main()
{
	int n = 0;
	scanf_s("%d", &n);
	int ret = Fib(n);
	printf("%d ", ret);
	return 0;
}

加循环的递归求斐波那契数列

#include<stdio.h>
int Fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 1;
	while (n > 2)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}
int main()
{
	int n = 0;
	scanf_s("%d", &n);
	int ret = Fib(n);
	printf("%d", ret);
	return 0;
}