侧边栏壁纸
博主头像
张种恩的技术小栈博主等级

行动起来,活在当下

  • 累计撰写 748 篇文章
  • 累计创建 65 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

C语言学习小记(7)-函数

zze
zze
2024-03-12 / 0 评论 / 0 点赞 / 23 阅读 / 16004 字

不定期更新相关视频,抖音点击左上角加号后扫一扫右方侧边栏二维码关注我~正在更新《Shell其实很简单》系列

概述

C语言中的函数是一个独立的、可重用的代码块,它执行特定任务并可以接受输入参数,并可能返回一个结果。函数是C语言程序设计的核心组件之一,它们有助于将大型复杂的程序分解为小的、易于管理及测试的部分。

定义与声明

函数定义包含了函数体(实际执行的代码)以及函数头,函数头中包括函数名、参数列表和返回类型。

返回类型 函数名(参数列表) {
    // 函数体(包含执行的语句)
}

例:

// 定义一个计算两数之和的函数,返回值类型为int,函数名为addNumbers,接受两个整型参数a和b
int addNumbers(int a, int b) {
    // 函数体,实现加法操作并返回结果
    int sum = a + b;
    return sum;
}

函数声明(也称为函数原型)提供给编译器函数的基本信息,以便在调用函数之前进行类型检查。声明通常出现在函数调用之前或在源文件头部。例:

// 声明addNumbers函数,告知编译器该函数的存在、返回类型以及参数列表
int addNumbers(int a, int b);

函数分类

  • 库函数:C语言标准库提供的内置函数,如数学函数abs()acos()等。

  • 自定义函数:程序员根据需求自定义的函数,用于完成特定功能。

参数传递

  • 实际参数(实参)是在调用函数时传入的实际值。

  • 形式参数(形参)是在函数定义中接收实参的变量。

例:

// 函数定义:声明一个名为addNumbers的函数,它有两个形参x和y,返回值类型为int
int addNumbers(int x, int y) {
    // 函数体:计算两个数之和,并返回结果
    return x + y;
}

int main() {
    // 定义两个整型变量作为实参
    int a = 10;
    int b = 20;

    // 函数调用:将a和b作为实参传递给addNumbers函数
    int result = addNumbers(a, b);

    // 输出函数调用的结果
    printf("The sum is: %d\n", result);

    return 0;
}

在这个例子中:

  • 形参:int xint y 是在函数 addNumbers 的定义中的形参。

  • 实参:当在 main 函数中调用 addNumbers(a, b) 时,ab 就是传递给 addNumbers 函数的实参,它们分别与形参 xy 对应。

返回值

函数可以有返回值,通过return语句从函数内部返回到调用处。例:

// 函数定义,该函数计算两数之积,并返回结果
int multiplyNumbers(int num1, int num2) {
    int product = num1 * num2;
    return product; // 返回乘积作为函数的结果
}

int main() {
    int m = 3;
    int n = 4;

    // 调用multiplyNumbers函数并将返回值打印出来
    int result = multiplyNumbers(m, n);
    printf("The product is: %d\n", result);

    return 0;
}

若函数没有返回值,则其返回类型应为void。例:

#include <stdio.h>

// 定义一个无返回值函数(void类型)
void welcomeMessage() {
    printf("Welcome to our program!\n");
    printf("We are glad you're here.\n");
}

int main() {
    // 调用无返回值函数
    welcomeMessage();

    return 0;
}

调用方式

传值调用:函数接收到的是实参的副本。例:

#include <stdio.h>

// 定义一个通过传值调用来计算两个整数之和的函数
int addNumbers(int a, int b) {
    // 计算两个整数的和
    int sum = a + b;

    // 返回结果
    return sum;
}

int main() {
    // 定义并初始化两个整数
    int num1 = 5;
    int num2 = 10;

    // 调用addNumbers函数,传递的是num1和num2的值
    int result = addNumbers(num1, num2);

    // 输出结果
    printf("The sum of %d and %d is: %d\n", num1, num2, result);

    return 0;
}

在这个示例中,addNumbers 函数接收两个 int 类型的参数 ab。在 main 函数中调用 addNumbers(num1, num2) 时,将实际参数 num1num2 的值复制给形式参数 ab。由于是传值调用,所以对 ab 的任何修改都不会影响到 main 函数中的 num1num2 值。函数计算两个参数的和后,将结果通过 return 语句返回给主调函数(这里是 main)。

传址调用:通过指针传递,函数可以直接修改实参所指向的内存区域。例:

#include <stdio.h>

// 定义一个通过传址调用来交换两个整数的函数
void swapNumbers(int* a, int* b) {
    // 临时保存其中一个数的值
    int temp = *a;

    // 将一个数的值赋给另一个数
    *a = *b;
    // 再将临时变量的值赋回原位置
    *b = temp;
}

int main() {
    // 定义并初始化两个整数
    int num1 = 5;
    int num2 = 10;

    printf("Before swap: num1 = %d, num2 = %d\n", num1, num2);

    // 调用swapNumbers函数,传入的是num1和num2的地址
    swapNumbers(&num1, &num2);

    printf("After swap: num1 = %d, num2 = %d\n", num1, num2);

    return 0;
}

在这个示例中,swapNumbers 函数接受两个 int 类型的指针作为参数,当我们从 main 函数调用它时,我们传递的是 num1num2 的地址(通过取地址符 & 获取)。在函数内部,通过对指针解引用 (*a*b) 来访问和修改原始变量的值,实现了不返回任何值但能改变外部状态的效果。

嵌套调用与递归

嵌套调用:在一个函数内部调用另一个函数。例:

#include <stdio.h>

// 定义一个函数A,它在内部调用了另一个函数B
void functionA(int n) {
    printf("Inside functionA\n");

    // 嵌套调用函数B
    functionB(n);
}

// 定义被嵌套调用的函数B
void functionB(int n) {
    if (n > 0) {
        printf("Inside functionB, n = %d\n", n);

        // 减小参数值并递归调用自身(这里演示嵌套而非递归)
        functionB(n - 1);
    }
}

int main() {
    int num = 3;

    // 调用函数A
    functionA(num);

    return 0;
}

functionA() 内部调用了 functionB(),这是一种直接的函数嵌套调用,但并没有形成递归关系。

递归调用:函数直接或间接地调用自身来解决问题。

#include <stdio.h>

// 定义一个计算阶乘的递归函数
int factorial(int n) {
    // 递归边界条件:0和1的阶乘都为1
    if (n == 0 || n == 1)
        return 1;

    // 递归调用自身
    return n * factorial(n - 1);
}

int main() {
    int number = 5;

    // 计算并输出5的阶乘
    printf("The factorial of %d is: %d\n", number, factorial(number));

    return 0;
}

factorial() 函数在其定义内部调用了自身,并通过一个或多个终止条件(即递归边界)来结束递归过程。在这个例子中,当 n 等于01时,递归停止,否则继续调用自身以计算下一个较小数的阶乘。

作用域和生命周期

函数内声明的变量只在该函数内部有效,具有局部作用域。例:

#include <stdio.h>

// 全局作用域的变量和函数声明
int globalVar = 10;
void globalFunction() {
    printf("Inside global function.\n");
}

void localFunction() {
    // 局部作用域的变量声明
    int localVar = 20;

    printf("globalVar inside localFunction: %d\n", globalVar);
    printf("localVar inside localFunction: %d\n", localVar);

    // 函数内部调用全局函数
    globalFunction();
}

int main() {
    // 在main函数中可以访问全局作用域的变量和函数
    printf("globalVar in main: %d\n", globalVar);
    
    // 调用局部作用域的函数
    localFunction();

    // 尝试访问局部作用域的变量会出错(在这个上下文中不存在)
    // printf("localVar in main: %d\n", localVar); // 这行代码会导致编译错误

    return 0;
}

在上述示例中:

  • globalVarglobalFunction 的作用域是全局的,它们在整个程序文件中都可见。

  • localVar 的作用域是 localFunction 函数内部,它只在该函数执行期间有效。在函数外部尝试访问 localVar 是非法的。

全局变量在整个程序范围内都是可见的。

在C语言中,局部静态变量具有特殊的生命周期,它的作用域仍然是局部的,但生命周期从其定义时开始直到程序结束。

#include <stdio.h>

void localStaticFunction() {
    // 局部静态变量声明,生命周期从第一次进入此函数开始至程序结束
    static int staticLocalVar = 0;

    staticLocalVar++;
    printf("staticLocalVar: %d (lifetime from first call until program ends)\n", staticLocalVar);
}

int main() {
    for (int i = 0; i < 5; ++i) {
        localStaticFunction();
    }

    return 0;
}

在上述示例中:

  • staticLocalVar 是一个局部静态变量,在 localStaticFunction 函数内定义,但它每次被调用时都会保留上次调用结束时的状态,即它的生命周期从首次进入函数开始,持续到整个程序运行结束。

内联函数与匿名函数

内联函数(inline)是一种优化手段,编译器尝试将其展开以减少函数调用开销。

// C++ 示例
#include <iostream>

// 内联函数声明和定义
inline int add(int a, int b) {
    return a + b;
}

int main() {
    std::cout << "The sum is: " << add(5, 7) << std::endl;
    return 0;
}

C99及之后的标准引入了 func_ _ 关键字用于定义匿名函数,但严格意义上的匿名函数(如Lambda表达式)不是C语言的特性,而是C++等其他语言支持的。

// C++ Lambda 示例
#include <iostream>

int main() {
    // 定义一个接受两个整数参数并返回其和的lambda表达式
    auto addLambda = [](int a, int b) -> int {
        return a + b;
    };

    // 使用lambda函数计算和并输出结果
    int sum = addLambda(5, 7);
    std::cout << "The sum using lambda is: " << sum << std::endl;

    return 0;
}

在C语言中没有原生支持内联函数和匿名函数的概念。尽管有一些编译器提供了对内联函数的扩展支持,但并不能像C++那样直接声明和使用lambda表达式。

通过使用函数,C语言程序得以模块化,提高了代码的复用性、可读性和可维护性。同时,函数使得逻辑结构更加清晰,有利于复杂问题的解决。

示例

#include <stdio.h>

// 从终端保存数字输入到数组
void inputToArray(int arr[], int n);
// 打印 int 数组
void printIntArray(int arr[], int n);
// 从小到大排序
void sortIntArray(int arr[], int n);

int main(int argc, char const *argv[])
{
    int arr[5] = {0};
    int n = sizeof(arr) / sizeof(arr[0]);
    inputToArray(arr, n);
    sortIntArray(arr, n);
    printIntArray(arr, n);
    return 0;
}

void inputToArray(int arr[], int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("请输入第 %d 个数字:\n", i + 1);
        scanf("%d", &arr[i]);
    }
    return;
}

void printIntArray(int arr[], int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return;
}

void sortIntArray(int arr[], int n)
{
    for (int i = 0; i < n - 1; ++i)
    { // 外层循环控制遍历次数
        for (int j = 0; j < n - 1 - i; ++j)
        { // 内层循环进行元素比较和交换
            if (arr[j] > arr[j + 1])
            { // 如果前一个元素大于后一个元素,则交换它们
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return;
}

0

评论区