目录
1.函数的解释
函数是将一个标识符(函数名)关联到一条复合语句(函数体)的 C 语言构造。每个 C 程序都从Main函数开始执行,也从它或者调用其他用户定义函数或库函数终止。
详细C官方函数部分链接:函数 - cppreference.com
//add是一个自定义函数
void add(int a, int b)
{
//把下面的数,传给a,b,在进行相加,相加结果直接打印出来
printf("x + y = %d", a + b);
}
2.C语言函数的分类
在C语言中,一般函数分为:“库函数”,“自定义函数”。库函数是指:C语言自带的函数。而自定以函数,顾名思义就是用户自己定义一个函数出来使用,要注意的是,在创建函数的时候一定要让人看得懂,就是说,你的函数必须是让人看得懂的函数,不然在日后的工作中是很麻烦的一件事情。
2.1库函数
库函数相关头⽂件:https://zh.cppreference.com/w/c/header
定义:库函数是C语言标准提供的函数,这些函数是已经弄好的,可以直接使用的,能够帮助我们在一些常见的任务中帮助我们完成。如:“输入输出”,"字符串处理这些"。
输入输出函数(stdio.h)
输入输出函数,其实你之前是见过,就是我们所熟知的头文件。stdio.h就是标准库函数,输入输出,输出:printf()函数用于输出。例如:
#include <stdio.h> //标准输入输出函数
int main() //main程序的入口
{
int num = 10;
printf("num的数字是:%d\n", num); //有了stdio.h才能使用printf()输出函数
}
输入函数,这个函数你也见过,并且你经常去使用,它就是scanf(),标准输入函数,有了头文件就可以使用它。例如:
2.2自定义函数
定义:在写代码时,我们根据需求去编写代码,遇到哪些特殊需求时,可以根据自己的需求来自定义函数,来满足这个需求,这些任务一般是库函数无法直接完成,或者是为了让程序分解,更容易管理。
2.2.1自定义函数的语法格式
返回值类型 自定义函数 (形式参数)
{
语句;
}
2.2.2自定义函数的实践
(1)第一题:欢迎光临
#include <stdio.h>
//无返回 自定义函数 print_welcome 打印欢迎
void PWC()
{
printf("欢迎光临!!!!\nC语言:详解函数");
}
int main()
{
PWC(); //调用函数
return 0;
}
(2)第二题 打印数字的平方
#include <stdio.h>
//返回值 自定义函数: print_squart
void PSquart(int num) //值传递(形参)
{
int squart = num * num;
printf("这个数字的平方是: %d", squart);
}
int main()
{
printf("请输入100以内的(算平方): ");
int number = 0;
scanf("%d", &number);
PSquart(number);
return 0;
}
(3)第三题 计算和
#include <stdio.h>
int PSum(int a, int b)
{
return a + b;
}
int main()
{
int a = 0;
int b = 0;
while (scanf("%d %d", &a, &b) != EOF)
{
long long result = PSum(a, b);
printf("%d+%d=%d", a, b, result);
}
return 0;
}
3.函数的参数
形式参数(形参):在函数定义中的参数称为形式参数。例如上面add函数中的a和b,它们在函数被调用时会被分配内存空间,用于接收传递过来的实际值。
实际参数(实参):在调用函数时传递给函数的参数称为实际参数。例如,如果在主函数中有int result = add(3,5);这里的3和5就是实际参数。实参的值会被复制到对应的形式参数种。
参数传递方式主要有值传递,在地址传递中的时候,我们的值传递中,这个实际参数的值复制到形式参数中,所以函数内部对形参的修改不会影响到实参。相反的如果时地址传递的话,值会被改变。
3.1值传递的表现 --- 形参
在值传递中,是无法改变值的,相反的地址传递可以改变值。
#include <stdio.h>
void modify(int x)
{
x = 10;
}
int main()
{
int num = 5;
modify(num);
// num的值仍然是5,因为是值传递
printf("%d", num);
return 0;
}
3.2地址传递的表现 --- 实参
地址传递时,我们是可以改变其值的。相反的值传递就不可以改变其值。
#include <stdio.h>
void modify(int *x)
{
*x = 10;
}
int main()
{
int num = 5;
modify(&num); //地址传递
printf("%d", num);
return 0;
}
3.3函数参数的使用
1)计算两个数的最大公因数(欧几里得算法)
#include <stdio.h>
int gcd(int a, int b)
{
if (b == 0)
{
return a;
}
else
{
return gcd(b, a % b);
}
}
int main()
{
int num1 = 24;
int num2 = 36;
int result = gcd(num1, num2);
printf("%d 和 %d 最大公约数是 %d\n", num1, num2,result);
return 0;
}
2)冒泡排序 -- 简单排序
#include <stdio.h>
void bubble_Sort(int arr[], int n)
{
int i, j, temp;
for (i = 0; i < n - 1; i++)
{
for (j = 0; j < n - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
int arr[] = { 1,9,5,3,7,8 };
int a = sizeof(arr) / sizeof(arr[0]);
bubble_Sort(arr, a);
for (int i = 0; i < a; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
4.函数的返回值
函数可以返回一个值调用函数,返回值的类型在函数头中指定,如一个add函数返回一个整数,如果函数不需要返回值,可以将返回类型指定为void。
4.1例子
一个题目中可以有多个return语句,但是每次调用函数时只会执行其中一个return语句
int max(int a, int b)
{
if (a > b)
{
return a;
}
else
{
return b;
}
}
5.函数的调用
函数在定义后,需要在其他地方,调用执行这功能,函数调用形式是函数名加上实参列表。
int main()
{
int result = add(2, 3);
// 这里调用了add函数,并将返回值存储在result中
return 0;
}
6.函数的作用域
6.1局部变量解释
局部变量:函数内部定义的变量称为局部变量。它们的作用域仅限函数内部,其实跟之前所学的局部变量和全局变量没有上面不同,举个例子,在add函数中的sum变量就是局部变量。当函数执行结束的时候,局部变量的内存就会被释放。
6.2全局变量解释
全局变量:在所有函数外部定义的变量称为全局变量。全局变量的作用域从定义位置开始到整个源文件结束。全局变量可以被多个函数访问和修改,但是过多的去使用全局变量会导致程序的可读性变差,所以不建议去过多的使用全局变量。
int a = 10;
void func()
{
//可以访问和修改a
a++;
}
7.函数的嵌套使用
在C语言中函数是可以被嵌套的,即一个函数可以调用另一个函数,而被调用的函数又可以调用其他函数。
7.1函数的嵌套实践
(1)判断闰年并输出该年每月的天数
编写函数判断给定年份是否为闰年,再编写一个函数输出该年份每个月的天数,通过函数嵌套实现。闰年的判断规则:能被 4 整除但不能被 100 整除,或者能被 400 整除。
#include <stdio.h>
//判断闰年函数
int is_leap_year(int year)
{
return (year % 4 == 0 && year % 100 != 0 || (year % 400 == 0));
}
//输出每月天数函数,在嵌套调用判断闰年函数
void print_days(int year)
{
int days[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
if (is_leap_year(year))
{
days[1] = 29;
}
for (int i = 0; i < 12; i++)
{
printf("Month %d : %d days\n", (i + 1), days[i]);
}
}
int main()
{
int year = 2025;
print_days(year); //值传递
return 0;
}
(2) 数字加密和解密
设计一个简单的数字加密和解密,加密的需求:数字加上5摸10取余,在江结果与原数字交换位置。解密是加密的逆过程。
#include <stdio.h>
// 加密函数
void encrypt(int num[], int size)
{
for (int i = 0; i < size; i++)
{
num[i] = (num[i] + 5) % 10;
if (num[i] >= 10)
{
int temp = num[i] % 10;
num[i] = num[i] / 10 + temp * 10;
}
}
}
// 解密函数,嵌套调用加密函数的部分逻辑
void decrypt(int num[], int size)
{
for (int i = 0; i < size; i++)
{
if (num[i] >= 10)
{
int temp = num[i] % 10;
num[i] = num[i] / 10 + temp * 10;
}
num[i] = (num[i] + 10 - 5) % 10;
}
}
int main()
{
int numbers[] = { 12, 34, 56, 78, 90 };
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("Original numbers: ");
for (int i = 0; i < size; i++)
{
printf("%d ", numbers[i]);
}
printf("\n");
encrypt(numbers, size);
printf("Encrypted numbers: ");
for (int i = 0; i < size; i++)
{
printf("%d ", numbers[i]);
}
printf("\n");
decrypt(numbers, size);
printf("Decrypted numbers: ");
for (int i = 0; i < size; i++)
{
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
8.递归
递归函数是指在函数的定义中调用自身的函数(就是自身调自身)。一个典型的例子是计算阶乘:
int factorial(int n)
{
if (n == 0 || n == 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
8.1详解递归
递归函数需要有一个终止条件(如n==0或n==1时返回
1
),否则函数会一直调用自身,导致栈溢出错误。递归函数的优点是可以使代码更加简洁,适用于解决具有递归性质的问题,如树的遍历、汉诺塔问题等。
8.2递归实践
(1)二叉树遍历
#include <stdio.h>
#include <stdlib.h>
//二叉树节点结构体
typedef struct TreeNode
{
int val;
struct TreeNode* left;
struct TreeNode* right;
} TreeNode;
//先序遍历
void preorderTraversal(TreeNode* root)
{
if (root == NULL)
{
return;
}
printf("%d ", root->val);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
//中序遍历
void inorderTraversal(TreeNode* root)
{
if (root == NULL)
{
return;
}
inorderTraversal(root->left);
printf("%d ", root->val);
inorderTraversal(root->right);
}
//后序遍历
void postorderTraversal(TreeNode* root)
{
if (root == NULL)
{
return;
}
postorderTraversal(root->left);
postorderTraversal(root->right);
printf("%d ", root->val);
}
//创建二叉树节点
TreeNode* createTreeNode(int val)
{
TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
node->val = val;
node->left = NULL;
node->right = NULL;
return node;
}
int main()
{
//二叉树示例
TreeNode* root = createTreeNode(1);
root->left = createTreeNode(2);
root->right = createTreeNode(3);
root->left->left = createTreeNode(4);
root->left->right = createTreeNode(5);
printf("先序遍历结果: ");
preorderTraversal(root);
printf("\n");
printf("中序遍历结果: ");
inorderTraversal(root);
printf("\n");
printf("后序遍历结果: ");
postorderTraversal(root);
printf("\n");
return 0;
}
(2)汉诺塔
#include <stdio.h>
// 汉诺塔移动函数
void hanoi(int n, char from, char aux, char to)
{
if (n == 1)
{
printf("将圆盘1从%c柱移动到%c柱\n", from, to);
return;
}
hanoi(n - 1, from, to, aux);
printf("将圆盘%d从%c柱移动到%c柱\n", n, from, to);
hanoi(n - 1, aux, from, to);
}
int main()
{
int num_disks = 3;
hanoi(num_disks, 'A', 'B', 'C');
return 0;
}