【C语言初阶】C语言函数全解析:编写高效代码的秘密武器
📝个人主页🌹:Eternity._
⏩收录专栏⏪:C语言 “ 登神长阶 ”
🤡往期回顾🤡:C语言分支与循环语句
🌹🌹期待您的关注 🌹🌹
❀函数
- 📒1. C语言中函数的分类
- 🎩库函数
- 🎈自定义函数
- 📙2. 函数的参数
- 🔍实际参数(实参)
- 🔄形式参数(形参)
- 📕3. 函数的调用
- 🌞传值调用
- 🌙传址调用
- ⭐调用练习
- 📚4. 函数的嵌套调用和链式访问
- 🌸嵌套调用
- 🌺链式访问
- 📜5. 函数的声明和定义
- 🍁函数声明
- 🍂函数定义
- 📝6. 函数递归
- 🏞️递归的两个必要条件
- 🧩递归练习
- 🌄递归与迭代
- 🧩递归练习
- 📖7. 总结
前言: 在探索编程世界的浩瀚星图中,C语言无疑是一颗璀璨夺目的星辰,它不仅奠定了现代计算机编程语言的基础,更是无数软件与系统背后的基石。自其诞生以来,C语言以其高效、灵活、接近硬件的特性,赢得了开发者们的广泛青睐与深厚情感。而在这门语言的浩瀚海洋中,函数(Function)则是航行者手中的罗盘与风帆,指引着代码的方向,驱动着程序的运行
函数,作为C语言中最基本也是最强大的构建块之一,它不仅仅是一段可以重复使用的代码集合,更是模块化编程思想的集中体现。通过定义函数,我们能够将复杂的任务分解成一系列简单、易于理解和维护的子任务,这不仅提高了代码的可读性和可维护性,还促进了代码的重用,使得软件开发过程更加高效有序
在本文中,我们将踏上一场关于C语言函数的探索之旅,揭开其神秘面纱,一窥其内部结构与工作原理。我们将从函数的基本概念出发,逐步深入到函数的定义、声明、调用以及参数传递等核心知识点,通过生动的例子和详细的解析,帮助读者构建起对C语言函数全面而深刻的理解
让我们一同踏上这段充满挑战与收获的C语言函数之旅吧!
📒1. C语言中函数的分类
- 库函数
- 自定义函数
🎩库函数
库函数是将一些常用到的函数编完放到一个文件(通常是库文件,如lib文件)里,供其他人或程序进行调用的方式。这些函数一般是编译器提供的,可以在C源程序中直接调用。库函数极大地方便了用户,同时也补充了C语言本身的不足
C语言常用的库函数:
- IO函数
- 字符串操作函数
- 字符操作函数
- 内存操作函数
- 时间/日期函数
- 数学函数
- 其他库函数
库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件
推荐一个C/C++查询库函数的网址:库函数
🎈自定义函数
自定义函数是编程中非常重要的一个概念,它允许程序员根据自己的需求创建特定的函数来执行一系列操作或计算。这些函数可以在程序中多次调用,提高了代码的重用性和可维护性,给程序员一个很大的发挥空间
函数的组成:
ret_type fun_name(para1, * ) { ......;//语句项 } ret_type 返回类型 fun_name 函数名 para1 函数参数
我们来简单定义几个函数
函数定义代码示例:
//获取最大值函数的设计 int get_max(int x, int y) { return (x > y) ? (x) : (y); } int main() { int num1 = 10; int num2 = 20; int max = get_max(num1, num2); printf("max = %d\n", max); return 0; }
// 实现数据交换的函数 // 实现成函数,但是不能完成任务 void Swap1(int x, int y) { // 这里涉及到函数的形参,当需要修改变量的值的时候,我们在数据传输中,需要传递它的指针 int tmp = 0; tmp = x; x = y; y = tmp; } //正确的版本 void Swap2(int* px, int* py) { int tmp = 0; tmp = *px; *px = *py; *py = tmp; } int main() { int num1 = 1; int num2 = 2; Swap1(num1, num2); printf("Swap1::num1 = %d num2 = %d\n", num1, num2); Swap2(&num1, &num2); printf("Swap2::num1 = %d num2 = %d\n", num1, num2); return 0; }
📙2. 函数的参数
🔍实际参数(实参)
- 真实传给函数的参数,叫实参。
- 实参可以是:常量、变量、表达式、函数等。
- 无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参
🔄形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效
上面 Swap1 和 Swap2 函数中的参数 x,y,px,py 都是形式参数。在main函数中传给 Swap1 的 num1 ,num2 和传给 Swap2 函数的 &num1 , &num2 是实际参数
内存分配
这里可以看到 Swap1 函数在调用的时候, x , y 拥有自己的空间,同时拥有了和实参一模一样的内容。所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝
📕3. 函数的调用
函数的调用是编程中的一个基本操作,它涉及到执行已经定义好的函数代码块。在调用函数时,程序会暂停当前执行点的代码,跳转到被调用函数的开始处执行,执行完毕后再返回到调用点继续执行后续的代码
🌞传值调用
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参
🌙传址调用
- 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式
- 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量
⭐调用练习
写一个函数判断一年是不是闰年
代码示例 (C语言):
// 写一个函数判断一年是不是闰年 int is_leap_year(int year) { if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) { return 1; } else { return 0; } } int main() { int year = 0; scanf("%d", &year); if (is_leap_year(year) == 1) { printf("%d是闰年\n", year); } else { printf("%d不是闰年\n", year); } return 0; }
写一个函数,每调用一次这个函数,就会将 num 的值增加1
代码示例 (C语言):
// 写一个函数,每调用一次这个函数,就会将 num 的值增加1 void add_num(int* num) { (*num)++; } int main() { int num = 0; for (int i = 0; i
📚4. 函数的嵌套调用和链式访问
函数的嵌套调用和链式访问是编程中常见的两种概念,它们在不同的编程语言和上下文中有着不同的表现形式,但核心思想都是为了提高代码的可读性和复用性
🌸嵌套调用
代码示例 (C语言):
void test1() { printf("hehe\n"); } void test2() { int i = 0; for (i = 0; i
注意:函数可以嵌套调用,但是不能嵌套定义
🌺链式访问
链式访问:把一个函数的返回值作为另外一个函数的参数
代码示例 (C语言):
#include int main() { char arr[20] = "hello"; int ret = strlen(strcat(arr, "world")); // strlen用于计算字符串的长度,遇到'\0'停止, 头文件是string.h // strcat用于将两个字符串连接起来 头文件是string.h printf("%d\n", ret); return 0; }
int main() { printf("%d", printf("%d", printf("%d", 43))); // 4321 return 0; }
注意:printf函数的返回值是打印在屏幕上字符的个数
📜5. 函数的声明和定义
在编程中,函数的声明(Declaration)和定义(Definition)是两个相关但不同的概念,它们共同支持函数的使用。这两个概念在不同的编程语言和上下文中可能有所差异,但基本概念是相似的
🍁函数声明
- 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数
声明决定不了
- 函数的声明一般出现在函数的使用之前。要满足先声明后使用
- 函数的声明一般要放在头文件中的
🍂函数定义
- 函数的定义是指函数的具体实现,交待函数的功能实现
代码示例 (C语言):
// test.h int Add(int x, int y); // test.c #include "test.h" int main() { printf("%d\n", Add(1, 2)); } //函数Add的实现 int Add(int x, int y) { return x + y; }
📝6. 函数递归
函数递归是一种在函数中调用自身来解决问题的编程技术。递归通过将问题分解成更小的、更易于解决的子问题,直到达到一个基本的、无需递归即可解决的边界情况(称为基准情况或基本情况)。递归函数必须有一个或多个基准情况,以避免无限递归调用导致的栈溢出错误
🏞️递归的两个必要条件
- 递归出口,存在限制条件,当满足这个限制条件的时候,递归便不再继续
- 每次递归调用之后越来越接近这个限制条件
🧩递归练习
接受一个整型值(无符号),按照顺序打印它的每一位
代码示例 (C语言):
// 接受一个整型值(无符号),按照顺序打印它的每一位 void print(int n) { if (n > 9) { print(n / 10); } printf("%d ", n % 10); } int main() { int num = 1234; print(num); return 0; }
编写函数不允许创建临时变量,求字符串的长度
代码示例 (C语言):
int Strlen(const char* str) { // 递归出口 if (*str == '\0') return 0; // 统计数据 else return 1 + Strlen(str + 1); } int main() { char* p = "abcdef"; int len = Strlen(p); printf("%d\n", len); return 0; }
🌄递归与迭代
🧩递归练习
求n的阶乘
代码示例 (C语言):
int factorial(int n) { if (n printf("%d\n", factorial(5)); return 0; } if (n printf("%d\n", fib(10)); return 0; } if (n == 3) count++; if (n printf("%d\n", fib(20)); printf("count: %d", count); return 0; } int result; int pre_result; int next_older_result; result = pre_result = 1; while (n 2) { n -= 1; next_older_result = pre_result; pre_result = result; result = pre_result + next_older_result; } return result; } int main() { printf("%d\n", fib(10)); return 0; }
- 函数的定义是指函数的具体实现,交待函数的功能实现
- 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数