C语言中的函数(详解)

article/2025/9/21 23:36:53

目录

1.函数是什么

2.c语言中函数的分类:

2.1. 库函数

2. 自定义函数

3. 函数的参数

3.1 实际参数(实参)

3.2 形式参数(形参)

4. 函数的调用:

4.1 传值调用

4.2 传址调用

5. 函数的嵌套调用和链式访问 

5.1 嵌套调用 

5.2 链式访问

6.函数的声明和定义

6.1 函数声明:

6.2 函数定义:

7. 函数递归

7.1 什么是递归? 

7.2 递归的两个必要条件 


1.函数是什么

在维基百科中,对于函数的定义是子程序子程序是一个大型程序中的某部分代码,由一个或多个语句块组成,他负责完成某项特定的任务,而且相较于其他的代码,具备相对的独立性。

一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软
件库。

2.c语言中函数的分类:

2.1. 库函数

为什么会有库函数呢?

早期的c语言是没有函数的,他只规定你语法,比如for循环之类的该怎么写然后各种各样的语法细节的规则非常规定的很清楚,就是你写出的代码能够编辑处理。但是当有一天呢,A想在屏幕上打印一个信息的时候A就实现了一个类似于printf1的函数的功能。那这个时候呢,B说,我也要一个打印的函数,于是他写了一个printf2,后来c说,他也想打印。那他呢,也写了一个printf3函数。

这些人都互相都不知道,所以我们看一下。当我们每个人都去实行打印这个功能的时候,这个代码时,就变得冗余了。

第二是什么呢?开发效率低,我们每个人都在重复的造论子,你写一个,他也写一个,别人再写一个。写的都是类似的功能,开发效率当然低了

第三个就是不标准,你写的他写的都是同一功能,但是这个实现方法可能不一样。参数也可能不一样,返回值类型也可能不一样。

所以基于以上原因。 那能不能把常用的一些功能实现成函数呢?那这个时候呢,就有了库函数的概念,这个函数只要把参数规定死了。 返回类型规定死了。函数名规定死了,那他的使用方法肯定就是一模一样了,库函数的出现让我们代码的其实开发效率的变高了。 代码的更加标准化了。

注:但是库函数的使用必须包含对应的头文件

这里有两个推荐学习的网站:cppreference.com

cplusplus.com - The C++ Resources Network

那怎么学习库函数呢?
这里我们简单的看看:http://www.cplusplus.com

这是c的库,我们在左边的部分可以很快的找到我们曾经用过的函数,那么我将用一个例子带着你来学习库函数。 

strcpy这个函数包含在string.h这个头文件里面,这个函数的参数需要两个指针,返回值是一个字符型指针,指针就是地址,char * strcpy ( char * destination, const char * source );

Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).

翻译过来就是将source指向的C字符串复制到destination指向的数组中,包括终止的空字符(并在该点停止)。包括'\0'字符。

Pointer to the destination array where the content is to be copied.

指向目标数组的指针,那儿目标数组的内容要被复制。

C string to be copied.

要被复制的c字符串

 返回值是destiination,destiination是一个字符型指针。

总结一句话,就是strcpy函数是将一个字符串复制到另一个字符串上去的。

2. 自定义函数

如果库函数能干所有的事情,那还要程序员干什么?所以更加重要的是自定义函数。自定义函数和库函数一样,有函数名,返回值类型和函数参数。但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。

函数的组成:

ret_type fun_name(para1, * )
{
statement;//语句项
}
ret_type 返回类型
fun_name 函数名
para1   函数参数

给个例子,一看便知。

3. 函数的参数

3.1 实际参数(实参)

真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。

3.2 形式参数(形参)

形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内
存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

形参和实参的名字可以一样,不影响什么。

4. 函数的调用:

4.1 传值调用

函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。

写一个函数可以交换两个整形变量的内容 

void swap(int p1, int p2)
{int tmp = 0;tmp = p1;p1 = p2;p2 = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d%d", &a, &b);printf("交换前,a = %d b = %d\n", a, b);swap(a, b);/*int p1 = &a;int p2 = &b;swap(p1, p2);*/printf("交换后,a = %d b = %d\n", a, b);return 0;
}

可以看出,我明明把参数传进去了,为什么参数却还没有交换呢?我们要用到重要的传址调用了。

4.2 传址调用

传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。

void swap(int* p1 , int* p2)
{int tmp = 0;tmp = *p1;*p1 = *p2;*p2 = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d%d", &a, &b);printf("交换前,a = %d b = %d\n", a, b);swap(&a, &b);/*int p1 = &a;int p2 = &b;swap(p1, p2);*/printf("交换后,a = %d b = %d\n", a, b);return 0;
}

实参中,我将a,b的地址给传了进去,形参又用p1和p2两个指针存储了a,b的地址,接着用 * 操作符找到了a,b的地址并修改了里面的内容。

5. 函数的嵌套调用和链式访问 

5.1 嵌套调用 

#include <stdio.h>
void new_line()
{printf("hehe\n");
}
void three_line()
{int i = 0;for (i = 0; i < 3; i++){new_line();}
}
int main()
{three_line();return 0;
}
int main()
{void test(){//......}return 0;
}

 这种就是嵌套定义,即在一个函数内部去定义函数,是不可以的。

函数可以嵌套调用,但是不能嵌套定义。

5.2 链式访问

把一个函数的返回值作为另外一个函数的参数。

这个为什么打印的是4321呢?我们要查一下printf的返回值。所以结果是4321也就不足为奇了。

6.函数的声明和定义

6.1 函数声明:

 程序在执行的时候是一步一步往下走的,下面这张图则是教科书上的写法,我们调用一个函数时,要有函数的声明,可如果函数写在了main函数的上方,则不余需要声明了。

 1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数
    声明决定不了。
2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
3. 函数的声明一般要放在头文件中的。

6.2 函数定义:

函数的定义是指函数的具体实现,交待函数的功能实现。 

6.3 工作中的写法 

我们是会把Add函数写成一个加法模块。在主函数中调用Add的头文件即可。各自分开写代码,会让逻辑变得很清晰。

 

 

 

那么为什么要把源文件和头文件分开呢?假如你是一个程序员,你只想别人使用你写的模块。但是却不想让别人怎么看你实现的,只是告诉他怎么使用,这个时候你可以将 .c 文件设置成静态库。也就是把函数声明给出去,函数怎么实现的隐藏起来。

接下来教你怎么操作。我们先将上面的add.h add.c 文件从项目中移除。

然后关闭当前的解决方案,新建一个add项目,再打开之前关闭的解决方案文件所在的位置,将add.h add.c剪切到add文件里面。

 

 

 如果,不愿意暴露代码。可以编译成静态库。

 

 用记事本打开add.lib文件,会发现全是乱的。 

 然后将add.lib文件 和 add.h文件都放到我们最初的解决方案的文件里面 

 

将add.h添加到test.c中,#pragma comment(lib,"add.lib") 导入静态库的意思。 

 

这样,我们就完成了。 这样做的好处,是可以将一些核心代码隐藏起来。 

其实,c语言中的库函数,你直接包含头文件就可以使用,但实际上你也是看不到这些库函数是怎么实现的,之所以是能直接使用,是vs这个编译器默认将这些库函数的静态库导入了。

7. 函数递归

7.1 什么是递归? 

程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的主要思考方式在于:把大事化小

7.2 递归的两个必要条件 

存在限制条件,当满足这个限制条件的时候,递归便不再继续。
每次递归调用之后越来越接近这个限制条件

我会在下一篇详细讲解递归的一些题目,希望各位看官点点赞吧 !


http://chatgpt.dhexx.cn/article/JLk2eNQM.shtml

相关文章

linux Ubuntu将默认bash修改为csh

Ubuntu将默认bash修改为csh 前言Linux系统中的shell版本问题修改方法bash切换csh方法 前言 为什么要将默认bash修改为csh&#xff0c;有时候安装的软件命令是基于csh写的&#xff0c;如果用bash使用软件就会报错&#xff0c;如&#xff1a;“No command ‘setenv’ found”&…

配置你的 csh/tcsh

配置你的 csh/tcsh 选择 csh/tcsh 和许多刚从 Linux 转到 BSD 的人不同&#xff0c;我并没有装完 BSD 就顺手安装 bash&#xff0c;因为之前除了打命令&#xff0c;我没有用到额外的功能&#xff0c;bash 也好&#xff0c;csh 也罢&#xff0c;在我眼里都是当做 shell 来用。但…

bash 和 tcsh(csh)的不同,带例子

我使用bash和tcsh(csh)过程中总结出的一些异同&#xff0c;附我的彩色的提示行配置 效果&#xff1a; 自做的彩色提示符 bash PS1 命令提示符 ## PS1\[\033[01;33m\][\D{%y-%m-%d} \t]\[\033[00m\]\[\033[01;32m\][\!]\[\033[00m\]${debian_chroot:($debian_chroot)}\[\03…

Bash与Csh的区别

zz &#xff1a;http://dangdanding.blog.163.com/blog/static/27992981201262595221896/ 一、csh的while循环控制结构及if then: #!/bin/csh -f while ($#argv > 1) if ("$1" "-s") then shift if ($#argv > 1) then set source …

CSH脚本学习

CSH脚本学习笔记(不常见的命令用法) csh中的 > ! 命令与bash中的> 和>| 命令相同&#xff0c;都是写入文件内容。但在bash中表示没有文件则不创建文件。 foreach var &#xff08; list ) command end 是CSH脚本中的循环命令&#xff0c;将list的值逐一赋值给变量var…

Shell编程之Csh和Bash的经验总结

文章目录 前言1. 变量和环境变量设置1. csh2. bash 2. if语句1. csh2. bash 3. while循环1. csh2. bash 4. 数组1. csh2. bash 5. 获取当前文件路径1. csh2. bash 6. 获取当前时间1. csh2. bash 7. 产生随机数并测试1. csh2. bash 8. 补充1. 查看系统默认用的 Shell2. 查看系统…

矩阵转置基本性质

一个矩阵的转置与本身相乘得到对称矩阵 一个矩阵的逆矩阵与本身相乘得到单位矩阵 行列式不等于零&#xff0c;矩阵可逆&#xff0c;反之不可逆 满秩矩阵一定是可逆的

Maple: 矩阵转置

在Maple中用%T的命令执行矩阵转置&#xff0c;具体效果如下

5*5矩阵转置

编写程序&#xff1a;对一个5X5的二维整型数组转置&#xff0c;即行列互换。 要求从键盘输入数组的值&#xff0c;输出转置前及转置后的结果。 如&#xff1a; 输入格式: 输入数组前有如图示的提示&#xff1b; 输出格式: 输出转置前及转置后的结果&#xff0c;输出前有提示…

矩阵转置输出

输入样例 3 2 1 2 3 4 5 6 #include<stdio.h> int main() {int m,n;int a[15][15]{0};//二维数组int i,j;scanf("%d %d",&m,&n);for(i0;i<m;i)for(j0;j<n;j)scanf("%d",&a[i][j]);//先全部输入再进行其他操作for(i0;i<m;i){for…

c语言函数矩阵转置代码,C语言实现矩阵转置

讲解对象&#xff1a;C语言实现矩阵转置 作者&#xff1a;融水公子 rsgz 1随机函数生成矩阵 #include #include #include int main(){ int i,j; int a[5][3]; printf("生成矩阵:\n"); //srand(time(NULL)); for(i0;i<5;i){ for(j0;j<3;j){ a[i][j]rand()%20; }…

vue报错:Failed to resolve directive: modle

问题原因&#xff1a;我把model写成了modle

[Vue warn]: Failed to resolve directive: modle (found in ComponentA)

报错原因&#xff1a;单词拼写错误 解决方法&#xff1a;检查是否将model写成了modle 温馨提示&#xff1a;编写代码要细心且严谨&#xff08;这已经是我第二次犯类似的错误了&#xff09;

Proteus找不到模型文件 Could not find the modle file

这一部分和你Proteus的文件夹地址一样 这些也要一样 且安装路径需要选择英文路径 同理 最后再用管理员模式打开一下就可以了。。。&#xff08;这些都是弱智问题&#xff09;&#xff08;我是弱智&#xff09;

Vue:解决[Vue warn]: Failed to resolve directive: modle (found in Anonymous)

解决问题 [Vue warn]: Failed to resolve directive: modle (found in <ComponentA>) console.error(("[Vue warn]: " msg trace)); 原因是 我把model 写成了 modle 这类错误一般是单词写错了 (found in <Anonymous>) 解决思路

modle bulinder 实践1.Excel坐标转面

1.结果&#xff1a; 1.工具放在内存里可以加快运行速度 2.步骤 a.Excel转表工具 b.创建XY事件图层&#xff08;选用投影坐标系--高斯克吕格-CGCS2000-34坐标系&#xff09; c.筛选工具<生成的是临时表&#xff1b;点转线需要真实存在的表&#xff1b;故进行筛选&#xf…

vue v-modle修饰符.number .trim

语法糖&#xff1a; 在不影响功能的情况下&#xff0c;添加某种方法实现同样的效果&#xff0c;从而方便程序开发。 .number&#xff1a;可以将输入转换成Number类型&#xff0c;否则虽然输入的是数字&#xff0c;但它的类型其实是String。 .trim&#xff1a;自动过滤输入的首尾…

Android Studio 导入modle是提示名称已存在

最近在做项目的时候&#xff0c;使用到别人的东西&#xff0c;就引入的相应的Modle&#xff0c;引入成功后。第二天打开项目的时候发现前一天引入的Modle不见了&#xff0c;再次引入的时候提示该Modle的名称已经存在&#xff1b;我查理一下资料解决方法如下&#xff1a; 其实Mo…

Action层, Service层 ,modle层 和 Dao层详解

一、SSH架构最基本的分层方式&#xff1a; modle层&#xff1a;就是对应的数据库表的实体类&#xff0c;即将现实对象抽象成类。 Dao层&#xff1a;是使用了Hibernate连接数据库、操作数据库&#xff08;增删改查&#xff09;和model层。 Service层&#xff1a;引用对应的Dao数…