函数就像是编程界的“外卖小哥”,帮你把任务分解成小块,然后把结果送回来。别担心,我会用幽默的方式带你飞驰在这个奇妙的世界里。咱们开始吧!
1. 函数定义与调用
外卖小哥的职责:
想象一下,你每天都要做饭,但每次都从头开始,那得多累啊!函数就像是你的“外卖小哥”,帮你把任务分解成小块,然后把结果送回来。定义函数就像是写好订单,调用函数就像是按下“下单”按钮。
代码示例:
#include <stdio.h>
// 定义一个函数,打印一句问候
void sayHello() {
printf("Hello, C语言世界!\n");
}
int main() {
// 调用函数,就像按下“下单”按钮
sayHello();
return 0;
}
运行结果:
Hello, C语言世界!
外卖小哥的视角:
-
函数定义: 写好订单(
void sayHello()
),告诉外卖小哥要送什么。 -
函数调用: 按下“下单”按钮(
sayHello()
),外卖小哥开始行动。 -
结果: 外卖小哥把“问候”送到你面前。
2. 函数声明与定义
外卖小哥的预告:
函数声明就像是给外卖小哥发消息:“嘿,我有个任务,稍后我会告诉你具体怎么做。”而定义则是详细说明任务的内容。
代码示例:
#include <stdio.h>
// 函数声明,告诉编译器:“嘿,我有个函数叫printNumber,稍后我会定义它。”
void printNumber(int num);
int main() {
// 调用函数,就像按下“下单”按钮
printNumber(42);
return 0;
}
// 函数定义,告诉编译器:“这就是printNumber的具体内容。”
void printNumber(int num) {
printf("我最喜欢的数字是:%d\n", num);
}
运行结果:
我最喜欢的数字是:42
外卖小哥的视角:
-
函数声明: 告诉外卖小哥:“我有个任务,稍后会详细说明。”
-
函数调用: 按下“下单”按钮,外卖小哥出发。
-
函数定义: 告诉外卖小哥任务的具体内容(比如送什么、送到哪里)。
-
结果: 外卖小哥把数字“42”送到你面前。
3. 参数传递:值传递和地址传递
外卖小哥的包裹:
参数传递就像是给外卖小哥的包裹。值传递是把包裹的副本交给小哥,地址传递则是把包裹的地址告诉他。
值传递(传递副本)
-
说明: 值传递就像是把一个包裹的副本交给外卖小哥,小哥可以修改副本,但不会影响原来的包裹。
代码示例:
#include <stdio.h>
// 定义一个函数,尝试修改传入的值
void tryToChange(int num) {
num = 99; // 修改副本,但不会影响原变量
printf("在函数里,num变成了:%d\n", num);
}
int main() {
int x = 42;
printf("调用函数前,x是:%d\n", x);
tryToChange(x); // 传递x的副本
printf("调用函数后,x还是:%d\n", x); // x的值没变
return 0;
}
运行结果:
调用函数前,x是:42
在函数里,num变成了:99
调用函数后,x还是:42
地址传递(传递地址)
-
说明: 地址传递就像是把包裹的地址交给外卖小哥,小哥可以直接修改包裹的内容。
代码示例:
#include <stdio.h>
// 定义一个函数,通过地址修改值
void changeValue(int* num) {
*num = 99; // 直接修改地址里的值
printf("在函数里,num变成了:%d\n", *num);
}
int main() {
int x = 42;
printf("调用函数前,x是:%d\n", x);
changeValue(&x); // 传递x的地址
printf("调用函数后,x变成了:%d\n", x); // x的值变了
return 0;
}
运行结果:
调用函数前,x是:42
在函数里,num变成了:99
调用函数后,x变成了:99
外卖小哥的视角:
-
值传递: 你把包裹的副本交给外卖小哥,小哥可以随便折腾副本,但你的原包裹不会变。
-
地址传递: 你把包裹的地址交给外卖小哥,小哥可以直接去你的包裹那里修改内容。
表格对比:
特性 | 值传递 | 地址传递 |
---|---|---|
传递内容 | 副本(外卖小哥拿到副本) | 地址(外卖小哥拿到包裹地址) |
修改影响 | 不影响原变量 | 直接修改原变量 |
使用场景 | 不需要修改原变量时 | 需要修改原变量时 |
4. 函数的返回值
外卖小哥的回礼:
函数的返回值就像是外卖小哥送回来的包裹,你可以用它,也可以忽略它。
代码示例:
#include <stdio.h>
// 定义一个函数,返回两个数的和
int add(int a, int b) {
return a + b; // 把结果“送”回去
}
int main() {
int result = add(5, 7); // 接收返回值
printf("5 + 7 = %d\n", result);
return 0;
}
运行结果:
5 + 7 = 12
外卖小哥的视角:
-
函数定义: 告诉外卖小哥任务(比如“把两个数相加”)。
-
函数调用: 按下“下单”按钮,外卖小哥开始执行任务。
-
返回值: 外卖小哥完成任务后,把结果(包裹)送回来。
-
结果: 你收到了外卖小哥送回来的结果(
12
)。
5. 函数的嵌套与递归
嵌套调用:外卖小哥的接力
-
说明: 嵌套调用就像是一个外卖小哥把任务交给另一个外卖小哥,完成后再把结果传回来。
代码示例:
#include <stdio.h>
void innerFunction() {
printf("我是内层函数,我是接力的外卖小哥!\n");
}
void outerFunction() {
printf("我是外层函数,我准备把任务交给内层函数...\n");
innerFunction(); // 把任务交给内层函数
printf("内层函数任务完成!\n");
}
int main() {
outerFunction();
return 0;
}
运行结果:
我是外层函数,我准备把任务交给内层函数...
我是内层函数,我是接力的外卖小哥!
内层函数任务完成!
递归函数:外卖小哥的自我复制
-
说明: 递归就像是外卖小哥接到任务后,自己复制了一个分身去完成子任务,直到任务结束。
代码示例:阶乘
#include <stdio.h>
// 计算阶乘的递归函数
int factorial(int n) {
if (n == 0) {
return 1; // 基本情况,任务结束
} else {
return n * factorial(n - 1); // 自己调用自己,复制分身去完成子任务
}
}
int main() {
int num = 5;
printf("%d的阶乘是:%d\n", num, factorial(num));
return 0;
}
运行结果:
5的阶乘是:120
代码示例:斐波那契数列
#include <stdio.h>
// 斐波那契数列的递归函数
int fibonacci(int n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else {
return fibonacci(n - 1) + fibonacci(n - 2); // 自己调用自己
}
}
int main() {
int num = 6;
printf("斐波那契数列的第%d项是:%d\n", num, fibonacci(num));
return 0;
}
运行结果:
斐波那契数列的第6项是:8
外卖小哥的视角:
-
嵌套调用: 外层外卖小哥把任务交给内层外卖小哥,内层完成后再把结果传回来。
-
递归调用: 外卖小哥接到任务后,复制一个分身去完成子任务,直到任务结束。
6.值传递与地址传递,递归调用
值传递(Pass by Value)
值传递是将变量的副本传递给函数。函数内部对参数的修改不会影响原始变量。
代码示例:
#include <stdio.h>
void changeValueByValue(int num) {
num = 99; // 修改副本,不影响原始变量
printf("函数内部(值传递):num = %d\n", num);
}
int main() {
int x = 42;
printf("调用前(值传递):x = %d\n", x);
changeValueByValue(x); // 传递x的副本
printf("调用后(值传递):x = %d\n", x); // x的值不变
return 0;
}
运行结果:
调用前(值传递):x = 42
函数内部(值传递):num = 99
调用后(值传递):x = 42
示意图描述:
+-------------------+ +-------------------+
| 主函数(main) | | 函数(changeValueByValue)|
| | | |
| x = 42 |----->| num = 42(副本) |
| | | |
+-------------------+ +-------------------+
-
关键点:
-
主函数中的
x
和函数中的num
是两个独立的变量。 -
函数内部对
num
的修改不会影响主函数中的x
。
-
地址传递(Pass by Address)
地址传递是将变量的地址传递给函数。函数通过地址直接修改原始变量。
代码示例:
#include <stdio.h>
void changeValueByAddress(int* num) {
*num = 99; // 通过地址修改原始变量
printf("函数内部(地址传递):*num = %d\n", *num);
}
int main() {
int x = 42;
printf("调用前(地址传递):x = %d\n", x);
changeValueByAddress(&x); // 传递x的地址
printf("调用后(地址传递):x = %d\n", x); // x的值被修改
return 0;
}
运行结果:
调用前(地址传递):x = 42
函数内部(地址传递):*num = 99
调用后(地址传递):x = 99
示意图描述:
+-------------------+ +-------------------+
| 主函数(main) | | 函数(changeValueByAddress)|
| | | |
| x = 42 |----->| num = &x(地址) |
| | | *num = 99 |
+-------------------+ +-------------------+
-
关键点:
-
主函数中的
x
和函数中的num
指向同一个内存地址。 -
函数通过
*num
修改了主函数中的x
。
-
递归调用(Recursive Call)
递归调用是函数调用自己的过程。递归的关键在于:
-
基准条件(Base Case):递归的终止条件。
-
递归步骤(Recursive Step):函数调用自身,逐步接近基准条件。
代码示例:阶乘函数
#include <stdio.h>
// 递归计算阶乘
int factorial(int n) {
if (n == 0) { // 基准条件
return 1;
} else { // 递归步骤
return n * factorial(n - 1);
}
}
int main() {
int num = 5;
printf("%d 的阶乘是:%d\n", num, factorial(num));
return 0;
}
运行结果:
5 的阶乘是:120
递归调用的示意图描述:
假设计算 factorial(3)
,递归调用过程如下:
factorial(3) = 3 * factorial(2)
= 3 * (2 * factorial(1))
= 3 * (2 * (1 * factorial(0)))
= 3 * (2 * (1 * 1)) // 基准条件:factorial(0) = 1
= 6
递归调用的图示:
factorial(3) ----> factorial(2) ----> factorial(1) ----> factorial(0)
(3 * ?) (2 * ?) (1 * ?) (1)
-
关键点:
-
每次递归调用都会生成一个新的函数调用栈。
-
每次调用都会逐步接近基准条件(
factorial(0)
)。 -
递归调用完成后,结果从内向外逐层返回。
-
总结:值传递、地址传递和递归调用
-
值传递:
-
传递变量的副本。
-
函数内部的修改不影响原始变量。
-
-
地址传递:
-
传递变量的地址。
-
函数通过地址直接修改原始变量。
-
-
递归调用:
-
函数调用自己。
-
需要基准条件和递归步骤。
-
递归调用会逐层深入,直到达到基准条件,然后逐层返回结果。
-
下面我将为你提供一个完整的C语言程序案例,这个程序会综合使用函数定义、函数调用、值传递、地址传递和递归调用等知识点。我们将通过一个简单的数学计算程序来展示这些概念。
程序案例:数学计算助手
这个程序将提供以下功能:
-
计算两个数的和。
-
交换两个数的值。
-
计算一个数的阶乘(递归实现)。
完整代码
#include <stdio.h>
// 函数声明
int add(int a, int b); // 计算两个数的和(值传递)
void swap(int* a, int* b); // 交换两个数的值(地址传递)
int factorial(int n); // 计算阶乘(递归调用)
int main() {
int x, y;
// 计算两个数的和
printf("请输入两个整数(用空格分隔):");
scanf("%d %d", &x, &y);
printf("两个数的和是:%d\n", add(x, y));
// 交换两个数的值
printf("\n交换两个数的值:\n");
printf("交换前:x = %d, y = %d\n", x, y);
swap(&x, &y); // 传递地址
printf("交换后:x = %d, y = %d\n", x, y);
// 计算一个数的阶乘
printf("\n请输入一个非负整数计算其阶乘:");
scanf("%d", &x);
if (x < 0) {
printf("阶乘计算仅支持非负整数!\n");
} else {
printf("%d 的阶乘是:%d\n", x, factorial(x));
}
return 0;
}
// 函数定义:计算两个数的和(值传递)
int add(int a, int b) {
return a + b;
}
// 函数定义:交换两个数的值(地址传递)
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// 函数定义:计算阶乘(递归调用)
int factorial(int n) {
if (n == 0) {
return 1; // 基准条件
} else {
return n * factorial(n - 1); // 递归步骤
}
}
程序功能解析
-
计算两个数的和(值传递)
-
函数
add
接收两个整数参数a
和b
,返回它们的和。 -
使用值传递,函数内部不会修改原始变量。
-
-
交换两个数的值(地址传递)
-
函数
swap
接收两个整数的地址,通过指针直接修改它们的值。 -
使用地址传递,函数可以修改主函数中的原始变量。
-
-
计算阶乘(递归调用)
-
函数
factorial
使用递归计算一个非负整数的阶乘。 -
基准条件是
factorial(0) = 1
,递归步骤是factorial(n) = n * factorial(n - 1)
。
-
运行示例
输入:
请输入两个整数(用空格分隔):3 5
请输入一个非负整数计算其阶乘:4
输出:
两个数的和是:8
交换两个数的值:
交换前:x = 3, y = 5
交换后:x = 5, y = 3
请输入一个非负整数计算其阶乘:4
4 的阶乘是:24
代码特点
-
函数的多样性:
-
程序中包含了值传递、地址传递和递归调用三种函数类型。
-
每个函数都有明确的功能,展示了函数的多种用途。
-
-
用户交互:
-
程序通过
scanf
和printf
与用户交互,接收输入并输出结果。
-
-
错误处理:
-
在阶乘部分,程序会检查用户输入是否为非负整数,避免非法输入。
-
总结
函数就像是你的“外卖小哥”,帮你分解任务、传递数据、返回结果,甚至自己调用自己。通过值传递和地址传递,你可以控制外卖小哥是折腾副本还是直接修改原包裹。嵌套调用和递归调用则让外卖小哥可以接力完成任务,或者复制分身去完成复杂的任务。
标签:函数,int,小哥,C语言,传递,num,外卖 From: https://blog.csdn.net/weixin_74149145/article/details/145286382