10. C语言 函数详解

news/2025/1/9 15:47:28 标签: c语言, 开发语言, ubuntu, linux, vim, vscode, c++

本章目录:

    • 前言
    • 1. C 语言函数概述
      • 1.1 函数的定义与结构
      • 1.2 函数声明
      • 1.3 函数调用
    • 2. 函数参数传递
      • 2.1 传值调用
      • 2.2 传引用调用(模拟)
      • 2.3 引用调用(C++ 特性)
    • 3. 内部函数与外部函数
      • 3.1 内部函数
      • 3.2 外部函数
      • 3.3 示例:多个文件中的函数使用
    • 4. 内联函数
      • 4.1 什么是内联函数?
    • 5. 函数的递归调用
      • 5.1 递归的优缺点
    • 6. 总结与最佳实践


前言

在 C 语言中,函数是程序的重要组成部分,它通过将任务划分为多个模块,使得代码更加结构化、可重用、可读性强。本文将从函数的定义、声明、调用、参数传递等多个角度进行详细分析,并结合读者的笔记,提供一些 C 语言函数的最佳实践和应用技巧。


1. C 语言函数概述

1.1 函数的定义与结构

一个 C 程序通常至少包含一个函数——main(),这是程序的入口函数。除了主函数,我们还可以定义其他辅助函数来执行具体任务,函数的定义格式如下:

return_type function_name(parameter_list) {
    // Function body
}
  • 返回类型:函数返回值的类型。如果函数不返回值,则使用 void
  • 函数名称:函数的标识符。
  • 参数列表:函数接收的输入参数,多个参数之间用逗号分隔。如果没有参数,留空即可。
  • 函数主体:包含具体执行的代码。

1.2 函数声明

函数声明(或称为函数原型)是在调用函数之前告知编译器该函数的返回类型、名称及参数列表。声明的作用是确保函数被正确调用。

return_type function_name(parameter_list);

对于以下函数定义:

int max(int num1, int num2) {
    if (num1 > num2) {
        return num1;
    }
    return num2;
}

其声明形式为:

int max(int num1, int num2);

1.3 函数调用

当函数被调用时,程序的控制权会转移到被调用的函数。函数执行完毕后,控制权会返回到调用点。调用时,如果函数有参数,需要传入实际参数。如果函数有返回值,可以将返回值存储在变量中。

int result = max(10, 20); // 调用函数

2. 函数参数传递

2.1 传值调用

C 语言默认使用传值调用(Pass by Value)。在传值调用中,函数接收到的是参数的副本,对副本的修改不会影响原始参数。

void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5, y = 10;
    swap(x, y);
    printf("x = %d, y = %d\n", x, y); // 输出 x = 5, y = 10
}

如上所示,尽管函数内交换了 ab 的值,但由于传值调用,xy 的值没有改变。

2.2 传引用调用(模拟)

C 语言没有原生的引用传递,但可以通过指针模拟传引用调用。传递参数的地址,使得函数可以直接修改原始数据。

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 5, y = 10;
    swap(&x, &y);
    printf("x = %d, y = %d\n", x, y); // 输出 x = 10, y = 5
}

2.3 引用调用(C++ 特性)

在 C++ 中,可以使用引用传递(Reference)来直接修改原始变量的值。在 C 语言中,引用传递无法直接实现,但可以通过指针来达到类似的效果。

3. 内部函数与外部函数

在 C 语言中,根据函数能否被其他源文件调用,函数可以分为内部函数外部函数

3.1 内部函数

如果一个函数只在当前源文件内有效,并且无法被其他源文件调用,那么它就是一个内部函数。要将函数定义为内部函数,可以在函数前加上 static 关键字:

static int max(int a, int b) {
    return (a > b) ? a : b;
}

3.2 外部函数

如果一个函数需要在多个源文件中被调用,通常会定义为外部函数。如果没有使用 static 关键字,C 编译器会默认认为函数是外部函数。如果函数在其他文件中调用,则需要使用 extern 关键字进行声明:

extern int max(int a, int b); // 外部函数声明

3.3 示例:多个文件中的函数使用

假设我们需要在多个文件中调用函数,可以通过 externstatic 实现模块化管理。

file1.c(主文件)

#include <stdio.h>

static void delete_string(char str[], char ch);

int main() {
    extern void enter(char str[]); // 外部函数声明
    extern void print(char str[]); // 外部函数声明
    char str[100];
    char c;
    enter(str);
    scanf("%c", &c);
    delete_string(str, c);
    print(str);
    return 0;
}

static void delete_string(char str[], char ch) {
    int i, j;
    for (i = j = 0; str[i] != '\0'; i++) {
        if (str[i] != ch) {
            str[j++] = str[i];
        }
    }
    str[j] = '\0';
}

file2.c(输入函数)

#include <stdio.h>

void enter(char str[100]) {
    fgets(str, 100, stdin); // 输入字符串
}

file3.c(输出函数)

#include <stdio.h>

void print(char str[]) {
    printf("%s\n", str);
}

4. 内联函数

4.1 什么是内联函数?

内联函数(inline)是一个特殊的函数,它的定义会被编译器直接嵌入到调用位置,从而避免函数调用的时间开销。内联函数适用于代码量小、调用频繁的场景。

inline void swap(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}

内联函数有以下几点需要注意:

  • 递归函数不能定义为内联函数。
  • 函数体应尽量简短,最好只包含简单的操作。

5. 函数的递归调用

递归是一种函数调用自身的技术。虽然递归能够简化代码,但它也可能带来较大的内存消耗和性能问题。下面是一个经典的递归求和的例子:

#include <stdio.h>

int sum(int n) {
    if (n == 1) {
        return 1;
    }
    return n + sum(n - 1); // 递归调用
}

int main() {
    printf("%d\n", sum(4)); // 输出 10
    return 0;
}

5.1 递归的优缺点

  • 优点:递归能够简化代码的逻辑,使得代码更加简洁。
  • 缺点:递归会消耗更多的内存,特别是递归深度较大的时候,可能导致栈溢出。

6. 总结与最佳实践

  • 函数声明与定义:提前声明函数,确保正确调用。
  • 参数传递:C 默认使用传值调用。传引用可以通过指针实现。
  • 函数重用与模块化:通过函数分割任务,增强代码的可读性与可维护性。
  • 内联函数:适用于频繁调用且代码简单的函数。
  • 递归调用:简化逻辑,但需谨慎使用,避免栈溢出。

C 语言的函数是构建高效、可维护程序的核心工具,通过合理划分任务和传递参数,可以大大提高代码的结构化和可复用性。希望本文能够帮助你更加深入地理解和使用 C 语言中的函数,写出更高效、简洁的代码。



http://www.niftyadmin.cn/n/5817680.html

相关文章

TensorRT-LLM中的MoE并行推理

2种并行方式&#xff1a; moe_tp_size&#xff1a;按照维度切分&#xff0c;每个GPU拥有所有Expert的一部分权重。 moe_ep_size: 按照Expert切分&#xff0c;每个GPU有用一部分Expert的所有权重。 二者可以搭配一起使用。 限制&#xff1a;二者的乘积&#xff0c;必须等于模…

腾讯云AI代码助手编程挑战赛-古诗词学习

一、作品介绍 在科技与文化深度交融的当下&#xff0c;“腾讯云 AI 代码助手编程挑战赛 - 每日古诗词” 宛如一颗璀璨的新星&#xff0c;闪耀登场。它绝非一场普通的赛事&#xff0c;而是一座连接编程智慧与古典诗词韵味的桥梁。 这项挑战赛以独特的视角&#xff0c;将每日古…

Jenkins-部署-windows

Jenkins-部署-windows 一: Jenkins 资料 一: Jenkins 资料

JAVA常见问题解答

1、面型过程与面向对象的区别&#xff1a; 抽象级别&#xff1a;面向过程主要关注解决问题的步骤和过程&#xff0c;以函数为基本单位&#xff0c;强调算法和流程控制。而面向对象则更关注问题领域中的实体和对象&#xff0c;强调将问题划分为多个相互关联的对象&#xff0c;并…

Perl语言的文件操作

Perl语言的文件操作 引言 Perl是一种强大的编程语言&#xff0c;广泛用于文本处理、系统管理、Web开发等诸多领域。其强大的正则表达式和文件操作能力使其在数据处理和脚本编写中备受青睐。本文将深入探讨Perl语言中的文件操作&#xff0c;包括文件的打开、读取、写入和关闭操…

攻防世界 bug

发现有Register界面&#xff0c;先去注册 登录以后发现以下界面&#xff0c;点击Manage显示you are not admin&#xff0c;并且在注册界面用admin为注册名时显示用户名已存在。初步推测是设法改变admin的密码取得权限。 在主界面一通操作并没有什么发现&#xff0c;去findpw…

详解MySQL 自动增加字段与 UUID区别

文章目录 前言原理自动增加字段UUID 优缺点自动增加字段UUID 应用场景自动增加字段UUID 案例及说明自动增加字段案例UUID案例 代码注释自动增加字段代码注释UUID代码注释 总结 前言 在数据库设计中&#xff0c;为表中的行生成唯一标识符是常见需求。MySQL提供了字段自动增加&a…

windows10下安装Microsoft SQL Server 2016

一、下载安装包 网站&#xff1a;MSDN, 我告诉你 - 做一个安静的工具站 选择需要的版本&#xff0c;点击详细信息&#xff0c;复制ed2k链接&#xff0c;打开eMule或迅雷&#xff0c;新建下载&#xff0c;粘贴链接&#xff0c;开始下载。 下载好的文件是一个.iso镜像文件。 二、…