函数
1.函数是什么
2.库函数
3.自定义函数
4.函数参数
5.函数调用
6.函数的嵌套调用和链式访问
7.函数的声明和定义
8.函数递归
库函数:其他大佬写的,可以让我们直接使用:
(1)标准库函数(涵盖在C语言内部的)
(2)第三方库函数:操作系统提供的函数,其他数不尽的第三方库例如github
我们自己写的:
自定义函数
函数组成:
ret_type fun_name(paral,*)
{
statement;//语句项
}
ret_type 返回类型
fun_name 函数名
paral 函数参数
代码执行到 "int ret = Max(10,20)"调用函数Max"int Max(int x,int y)"令"x = 10 y = 20"代码往下执行if判断后return返回"y"继续回到"int ret = Max(10,20)".
其中函数调用中的"Max(10,20)"括号中的10,20是实参即实际参数
函数中Max(int x,int y)当中的"x""y"是形参即形式参数
上图的函数名 Max 不能写作 max .因为stdlib.h这个头文件中,已经定义了一个max的宏,如果再定义一个名叫max的函数名字就冲突了.同理min也一样.
VS 中跳转到定义: Ctrl +鼠标左键 / F12 查看一下max宏的定义:
执行以下代码会发现语法没有问题但是x和y的值并不能互换 这是为什么?
(1)void Swap(int x, int y){
int tmp = x;
x = y;
y = tmp;
}
int main(){
int a = 10;
int b = 20;
Swap(a, b);
printf("a = %d,b = %d\n", a, b);
函数的形参和实参的关系:形参是实参的一份拷贝(副本)
//以下是函数调用的等价代码
int x = a;
int y = b;
int tmp = x;
x = y;
y = tmp;
我们会发现一番操作下来我们在使"x"和"y"的值发生变化而"a""b"其实没有发生变化.
怎么解决呢?我们尝试使用参数传指针的方式,可以做到让函数内部来操作 a 和 b .
(2)void Swap(int* x, int* y){
int tmp = *x;
*x = *y;
*y = tmp;
}
int main(){
int a = 10;
int b = 20;
Swap(&a, &b);
如何实现的呢,看一下等价代码:
int main(){
int a = 10;
int b = 20;
//Swap(&a, &b);
int* x = &a;
int* y = &b;
// *x 就是根据 a 的地址找到了 a 这个变量
// *x 就和 a 是等价的.
// *y 就和 b 是等价的
// 对 *x 和 *y 的交换,就是对 a 和 b 的交换.
int tmp = *x;
*x = *y;
*y = tmp;
指针也是一种变量.变量里存的是一个整数.这个整数具有特殊的含义,是内存中的一个地址,根据这个地址找到这块内存,也就能读取或修改这个内存的内容了.
还有觉得如果把函数中改成(3) int* tmp = x;
x = y;
y = tmp;是不是也能实现呢?其实效果和刚开始一样,依然是x和y的值在改变a和b的值依然没有发生改变.
为加深理解作图表示:下图为第一种情况下参数的变化情况:
下图为第二种情况下借助解引用操作,使用指针.参数的变化情况:
下图为第三种设想下参数情况:
参数传指针的方式可以让一个函数内部修改函数外部的变量.这样的参数称为"输出型参数".
写一个经典找素数的代码:(素数只能被1和自身整除)
#include<stdio.h>
#include<stdlib.h>
//如果返回 1 认为是素数.如果返回 0 ,认为不是素数.
int Isprise(int num){
if (num == 0){
return 0;
}if (num == 1){
return 1;
}
//找一下num能被哪些数整除.
//找反例,只要找到一个数字能被 num 整除,说明他不是素数.
for (int i = 2; i <= num; i++){
if (num % i == 0){
return 0;
}
}
return 1;//注意它在for循环完全运行完之后才返回1
}
int main(){
printf("%d\n", Isprise(97));
system("pause");
return 0;
}
写一个判断闰年的代码(被4整除的是闰年,被100整除但被400整除不是闰年)
#include<stdio.h>
#include<stdlib.h>
//如果返回 1 认为是素数.如果返回 0 ,认为不是素数.
int Isprise(int num){
if (num == 0){
return 0;
}if (num == 1){
return 1;
}
//找一下num能被哪些数整除.
//找反例,只要找到一个数字能被 num 整除,说明他不是素数.
for (int i = 2; i <= num; i++){
if (num % i == 0){
return 0;
}
}
return 1;
}
int IsleapYear(int year){
//世纪闰年和普通闰年
if (year % 100 == 0){
if (year % 400 == 0){
return 0;
}
return 1;
}
else{
//普通闰年
if (year % 4 == 0){
return 1;
}
return 0;
}
}
int main(){
//printf("%d\n", Isprise(99))
printf("%d\n", IsleapYear(1998));
system("pause");
return 0;
}
写一个函数实现有序数组的二分查找
(数据结构的 链表相关问题,排序相关问题,以及这种二分查找问题都要可以在紧张疲惫状态下熟练地在纸上写出来)
发现计算数组长度时在函数内与函数外计算结果不同
int binarysearch(int arr[], int toFind){
int left = 0;
int right = sizeof(arr) / sizeof(arr[0]) - 1;//最后一个元素的下标
printf("right = %d\n", right);
return 0;
}
int main(){
int arr[4] = { 1, 2, 3, 4 };
int size = sizeof(arr) / sizeof(arr[0]);
printf("size = %d\n", size);
int ret = binarysearch(arr, 1);
system("pause");
return 0;
}
这是C语言中的一个bug,数组作为函数参数的时候,会隐式转成指针,指向数组首元素的指针.
C语言中把指针和数组搞在一起了.
1.数组很多时候都能隐式转成指针.
2.有时候指针的使用和数组非常相似,也能[]取下标.
这都是C语言的bug.之所以不改是为了保证兼容性.
#include<stdio.h>
#include<stdlib.h>
//如果返回 1 认为是素数.如果返回 0 ,认为不是素数.
int Isprise(int num){
if (num == 0){
return 0;
}if (num == 1){
return 1;
}
//找一下num能被哪些数整除.
//找反例,只要找到一个数字能被 num 整除,说明他不是素数.
for (int i = 2; i <= num; i++){
if (num % i == 0){
return 0;
}
}
return 1;
}
int IsleapYear(int year){
//世纪闰年和普通闰年
if (year % 100 == 0){
if (year % 400 == 0){
return 0;
}
return 1;
}
else{
//普通闰年
if (year % 4 == 0){
return 1;
}
return 0;
}
}
//通过这个函数来完成二分查找
//数组是有序的
int binarysearch(int arr[], int size,int toFind){
int left = 0;
int right = size - 1;//最后一个元素的下标
// [left,right] 前闭后闭区间待查找区间
while (left <= right){
int mid = (left + right) / 2;
if (arr[mid] > toFind){
//左边找
right = mid - 1;
}
else if (arr[mid] < toFind){
//右边找
left = mid + 1;
}
else{
//找到了
return mid;
}
}
return -1;
}
int main(){
int arr[4] = { 1, 2, 3, 4 };
int size = sizeof(arr) / sizeof(arr[0]);
printf("size = %d\n", size);
int ret = binarysearch(arr, size,1);
system("pause");
return 0;
}
调用一次函数就让num加1(还是要注意形参和实参的关系)
void func(int* x){
*x += 1;
}
int main(){
int num = 0;
func(&num);
printf("num = %d\n", num);
system("pause");
return 0;
}
函数的嵌套调用和链式访问
嵌套调用:函数里可以调用其他函数.
链式访问:把一个函数的返回值作为另一个函数的参数.
函数的声明和定义
函数声明:extern 关键字,extern是可以省略的,但不建议省略,他没有函数体,以";"结束,声明相当于一个空头支票.
#include <stdio.h>
#include <stdlib.h>
//函数声明,这里的形参名也可以不写
extern double Max(double x, double y);
int main(){
printf("%f\n", Max(10.0, 20.0));
system("pause");
return 0;
}
//返回 x 和 y 中的最大值
//函数的定义
double Max(double x, double y){
if (x > y){
return x;
}
return y;
}
如果声明太多可以直接创建一个"calc.h"文件把声明全部放在里面,在".c"文件中使用头文件"#include "calc.h""
函数递归
函数的调用执行过程.
函数调用就会进入到函数体内部执行.遇到 return 语句,函数就结束了,回到调用位置继续执行.
一个函数既然可以调用别的函数,它如果调用自己就叫递归.
概念虽然简单,但实际代码执行过程中可能会很复杂.
1234 => 1 2 3 4
#include <stdio.h>
#include <stdlib.h>
void printNum(unsigned int num){
if (num > 9){
printNum(num / 10);
}
printf("%d ", num % 10);
}
int main(){
printNum(1234);
system("pause");
return 0;
}