首页 > 编程语言 >C语言的那点事第五篇:编程界的“外卖小哥”函数

C语言的那点事第五篇:编程界的“外卖小哥”函数

时间:2025-01-21 18:56:28浏览次数:3  
标签:函数 int 小哥 C语言 传递 num 外卖

函数就像是编程界的“外卖小哥”,帮你把任务分解成小块,然后把结果送回来。别担心,我会用幽默的方式带你飞驰在这个奇妙的世界里。咱们开始吧!


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)

递归调用是函数调用自己的过程。递归的关键在于:

  1. 基准条件(Base Case):递归的终止条件。

  2. 递归步骤(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))。

    • 递归调用完成后,结果从内向外逐层返回。


总结:值传递、地址传递和递归调用

  1. 值传递:

    • 传递变量的副本。

    • 函数内部的修改不影响原始变量。

  2. 地址传递:

    • 传递变量的地址。

    • 函数通过地址直接修改原始变量。

  3. 递归调用:

    • 函数调用自己。

    • 需要基准条件和递归步骤。

    • 递归调用会逐层深入,直到达到基准条件,然后逐层返回结果。


下面我将为你提供一个完整的C语言程序案例,这个程序会综合使用函数定义、函数调用、值传递、地址传递和递归调用等知识点。我们将通过一个简单的数学计算程序来展示这些概念。


程序案例:数学计算助手

这个程序将提供以下功能:

  1. 计算两个数的和。

  2. 交换两个数的值。

  3. 计算一个数的阶乘(递归实现)。


完整代码

#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); // 递归步骤
    }
}

程序功能解析

  1. 计算两个数的和(值传递)

    • 函数 add 接收两个整数参数 ab,返回它们的和。

    • 使用值传递,函数内部不会修改原始变量。

  2. 交换两个数的值(地址传递)

    • 函数 swap 接收两个整数的地址,通过指针直接修改它们的值。

    • 使用地址传递,函数可以修改主函数中的原始变量。

  3. 计算阶乘(递归调用)

    • 函数 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

代码特点

  1. 函数的多样性:

    • 程序中包含了值传递、地址传递和递归调用三种函数类型。

    • 每个函数都有明确的功能,展示了函数的多种用途。

  2. 用户交互:

    • 程序通过 scanfprintf 与用户交互,接收输入并输出结果。

  3. 错误处理:

    • 在阶乘部分,程序会检查用户输入是否为非负整数,避免非法输入。


总结

函数就像是你的“外卖小哥”,帮你分解任务、传递数据、返回结果,甚至自己调用自己。通过值传递和地址传递,你可以控制外卖小哥是折腾副本还是直接修改原包裹。嵌套调用和递归调用则让外卖小哥可以接力完成任务,或者复制分身去完成复杂的任务。

标签:函数,int,小哥,C语言,传递,num,外卖
From: https://blog.csdn.net/weixin_74149145/article/details/145286382

相关文章

  • C语言中的二维数组
    1.二维数组的定义类型说明符数组名 [常量表达式][常量表达式];(1).类型说明符      表示二维数组中数据元素的类型 (2).数组名          标识符 (3).[常量表达式][常量表达式]      第1维       第2维   ......
  • C语言程序设计十大排序—冒泡排序
    文章目录1.概念✅2.冒泡排序......
  • C语言编译
    C语言编译是把C语言编写的源代码转换为计算机能执行的机器码的过程。 首先需要一个文本编辑器来写代码,比如Vim、Notepad++等。代码写好后,使用C编译器,常见的有GCC(GNUCompilerCollection)。以GCC为例,如果有一个名为 main.c 的源文件,在命令行中输入 gccmain.c-ooutput ......
  • 5、原来可以这样理解C语言_数组
    目录​编辑1.数组的概念2.⼀维数组的创建和初始化2.1数组创建⼀维数组创建的基本语法如下:2.2数组的初始化2.3数组的类型3.⼀维数组的使⽤ 3.1数组下标3.2数组元素的打印3.3数组的输⼊4.⼀维数组在内存中的存储5.sizeof计算数组元素个数6.⼆维数组......
  • 一文告诉你Linux下如何用C语言实现ini配置文件的解析和保存
    嵌入式项目开发中,会有很多功能模块需要频繁修改参数,Linux下我们可以通过ini格式的文件保存配置信息。本文通过开源库iniparser,详细讲解如何用C语言实现ini文件的参数解析和配置保存。本文代码实例获取方式见文末。一、ini文件1什么是ini文件INI(InitializationFile)文件是......
  • C语言逆序操作数组和引用传递参数
    ////main.c//Test_C////Createdbystevexiaohuzhaoon2025/1/20.//#include<stdio.h>//C语言指针传递参数(引用传递)voidswap(int*px,int*py){intt=*px;*px=*py;*py=t;}voidtest(intn){intx=1;for(inti......
  • C语言实现顺序存储线性表
    ////Createdbystevexiaohuzhaoon2025/1/20.///****线性表的顺序存储结构实现*特点:逻辑上相邻的元素,物理上也相邻**/#include<stdio.h>#include<stdlib.h>#defineMAXSIZE100//定义线性表的最大长度//1.定义图书结构体Booktypedefstr......
  • 指针应用-查找数组元素(PTA)C语言
    编写一个名为findX的函数,该函数的参数p指向一个int数组,数组的容量n由参数2指定。在该数组中,查找数据x所在的位置。如果数据x有出现多次,则返回其最后一次出现的位置对应的下标。如果没有找到,则固定返回-2。intfindX(int*p,intn,intx);函数接口定义:intfindX(int*p,intn......
  • C语言:分支语句详解
           所谓分支,就是在不同情况下输出不同结果。下面我们来学习分支语句:1.if语句1.1if    if语句的书写方法如下:if(表达式)语句       如果表达式值为真,那么我们就执行语句,若表达式值不为真(为假),就不执行。在C语言中,我们说非0为真,0为假。我......
  • C语言的应用|猜数字游戏
    目录1.引言2.rand(包含在中)3.srand(包含在中)4.time(包含在中)5.游戏代码showtime1.引言  哈喽,大家好,好久不见。今天小邓儿,将带咱们用C语言,来写一个小游戏——猜数字。不过,编写游戏之前。先给大家拓展一些相关知识点(●'◡'●)2.rand(包含在<stdlib.h>中)1.1 ......