首页 > 其他分享 >【C语言】详解函数

【C语言】详解函数

时间:2024-08-26 22:26:03浏览次数:10  
标签:调用 return 函数 int C语言 详解 printf 库函数

文章目录


前言

一、函数的概念
二~三、自定义函数(形参和实参、return语句)和库函数(标准库和头文件)
四、函数的声明和定义(函数先声明,后使用)
五~六、传值调用和传址调用、嵌套调用和链式访问


一、 函数的概念

数学中我们其实就见过函数的概念,比如:⼀次函数 y=kx+b ,k和b都是常数,给⼀个任意的x,就得到⼀个y值。
其实在C语言也引入函数的概念,C语言中的函数就是⼀个完成某项特定的任务的⼀小段代码。这段代码是有特殊的写法和调用方法的。
C语言的程序其实是由无数个小的函数组合而成的,也可以说:⼀个大的计算任务可以分解成若干个较小的函数(对应较小的任务)完成。同时⼀个函数如果能完成某项特定任务的话,这个函数也是可以复用的,提升了开发软件的效率。

在C语言中我们⼀般会见到两类函数:
自定义函数:用户自己定义的函数
库函数:由系统提供的函数,用户不必自己定义,包含头文件后就可直接使用

二、自定义函数

每个 C 语言程序不管有多少行代码,都是从 main 函数开始执行的, main 函数是程序的入口,main 函数也被叫做:主函数。如果我们把所有的程序代码都写在主函数中,就会使主函数变得非常冗杂,使代码的阅读和维护变得困难。而且,有时程序中要多次实现某一功能(例如打印数组中的每一个元素),就需要多次重复编写实现此功能的程序代码,这使得程序冗长、不精炼。
我们可以将主函数中会多次使用到的功能包装成一个函数,这就是用户自定义函数。当主函数每次要使用这一功能时直接调用这个自定义函数即可,这使得主函数变得精简,增加代码可读性。

示例(打印数组排序前和排序后的内容):

#include <stdio.h>
#include <stdlib.h>
//qsort函数和配合其使用的自定义函数的具体使用方法和运行暂不需了解,此处使用仅为例子需要
int comper_int(const void* p1, const void* p2)//这是一个自定义函数,配合qsort函数使用的
{
	return *(int*)p1 - *(int*)p2;
}

int main()
{
	int arr[10] = { 1,3,5,7,9,2,4,6,8,10 };
	int i = 0;
	for (i = 0; i < 10; i++)//打印排序前的数组
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	qsort(arr, 10, 4, comper_int);//快速排序函数(库函数):作用是给一组数据按大小排序
	for (i = 0; i < 10; i++)//打印排序后的数组
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

我们发现以下这段打印数组中每个元素的代码在主函数中反复出现:

for (i = 0; i < 10; i++)
{
printf(“%d “, arr[i]);
}
printf(”\n”);

我们可以将以上这段代码包装成一个自定义函数,当主函数要使用打印数组中每个元素这一功能时直接调用这个自定义函数即可,这会使得主函数变得非常精简,如下:

#include <stdio.h>
#include <stdlib.h>

int comper_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

void printf_arr(int arr[],int n)//这段代码被包装成了一个自定义函数,这个自定义函数是专门用来打印数组中每个元素的
{                               //每当主函数要使用这一功能时直接调用这个自定义函数即可
	int i;
	for (i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()//我们可以看到主函数变得非常精简,增强了代码可读性
{
	int arr[10] = { 1,3,5,7,9,2,4,6,8,10 };
	printf_arr(arr, 10);//直接调用这个自定义函数即可打印数组中每个元素
	qsort(arr, 10, 4, comper_int);
	printf_arr(arr, 10);
	return 0;
}

在这里插入图片描述

1.函数的语法形式

ret_type fun_name(形式参数)
{

}

• ret_type 是函数返回类型
• fun_name 是函数名
• 括号中放的是形式参数
• { }括起来的是函数体

2. 形参和实参

#include <stdio.h>

int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}

int main()
{
	int a = 4;
	int b = 6;
	//调⽤加法函数,完成a和b的相加。求和的结果放在r中
	int r = Add(a, b);
	printf("%d\n", r);
	return 0;
}

在上面代码中,第3~8行是 Add 函数的定义,有了函数定义后,再第15行调用Add函数的。我们把第15行调用Add函数时,传递给函数的参数a和b,称为 实际参数,简称实参。实际参数就是真实传递给函数的参数。
在上面代码中,第3行定义函数的时候,在函数名 Add 后的括号中写的 x 和 y ,称为 形式参数,简称形参。
为什么叫形式参数呢? 实际上,如果只是定义了 Add 函数,而不去调用的话, Add 函数的参数 x和 y 只是形式上存在的,不会向内存申请空间,不会真实存在的,所以叫形式参数。形式参数只有在函数被调用的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程就是形参的实例化。

实参和形参的关系:虽然我们提到了实参是传递给形参的,他们之间是有联系的,但是形参和实参各自是独立的内存空间。
这个现象是可以通过调试来观察的。请看示例代码的调试演⽰:
在这里插入图片描述

我们在调试的时候可以观察到,x和y确实得到了a和b的值,但是x和y的地址和a和b的地址是不⼀样的,所以我们可以理解为形参是实参的⼀份临时拷贝。

3. return语句

return 英文译为返回,在函数中的任何地方都可以使用 return 语句。它的作用是使函数终止并且立即返回返回值,不再执行该函数后续代码。

return语句使用时的注意事项:

• return后边可以是一个数值,也可以是一个表达式,如果是表达式则先执行表达式,再返回表达式的结果。
• return返回的值的类型和函数返回类型应该保持一致。如果return返回的值和函数返回类型不一致,系统会自动将返回的值隐式转换为函数的返回类型。
• return后边也可以什么都没有,直接写 return; 这种写法只适合函数返回类型是void的情况。
• return语句执行后,函数就彻底返回,后边的代码不再执行。

三、库函数

1.标准库和头文件

C语言标准中规定了C语言的各种语法规则,C语言并不提供库函数;C语言的国际标准ANSIC规定了⼀些常用的函数的标准,那不同的编译器厂商根据ANSI提供的C语言标准就给出了⼀系列函数的实现。实现的这一系列函数被称为库函数,这些函数组成了⼀个函数库,被称为标准库
比如printf 、 scanf 都是库函数,库函数也是函数,不过这些函数已经是现成的,我们只要学会就能直接使用了。有了库函数,⼀些常见的功能就不需要程序员自己实现了,一定程度提升了效率;同时库函数的质量和执行效率上都更有保证。
各种编译器的标准库中提供了数量庞大的库函数,这些库函数根据功能的划分,都在不同的头文件中进行了声明。同⼀个系列的库函数⼀般会声明在同⼀个头文件中,要想使用特定的库函数,我们只要要在代码的开头用 #include 包含其对应的头文件。

以下是C标准库中包含的一些头文件(库函数相关头文件的链接: link):
在这里插入图片描述
每个头文件中都包含了一个系列或几个不同系列的库函数提供给程序员使用。例如:
<stdio.h>头文件提供标准输入输出函数,如printf、scanf、puts、gets等。<string.h>头文件提供字符串处理函数,如strcpy、strcat、strlen、strcmp等。

2.库函数的使用

#include <stdio.h>//在使用printf库函数之前,在代码的开头用 #include包含其对应的头文件

int main()
{
	int a = 4;
	int b = 6;
	int r = a + b;
	printf("%d\n", r);//包含其对应的头文件后,就可以直接使用printf库函数
	return 0;
}

在这里插入图片描述
想了解各种库函数的具体作用和使用方法可以通过以下两个链接进行学习:

• C/C++官方链接:link
• cplusplus.com:link

四、函数的声明和定义

C语言要求,在程序中用到的所有函数,都必须 “先声明,后使用”

比如:

#include <stdio.h>

int Add(int x, int y)//Add函数的定义
{
	int z = 0;
	z = x + y;
	return z;
}

int main()
{
	int a = 4;
	int b = 6;
	int r = Add(a, b);//调用Add函数
	printf("%d\n", r);
	return 0;
}

把函数的定义放在函数调用之前,是没什么问题的,编译器不会报错。
因为函数的定义是⼀种特殊的声明,满足函数“先声明,后使用”的规则,所以要是函数定义放在调用之前就无需对函数进行额外的声明了。

但要是把函数的定义放在函数调用之后,编译器就会报警告,如下:
在这里插入图片描述
因为在调用Add函数之前没有进行对Add函数的声明,这不满足函数“先声明,后使用”的规则,所以编译器就会报警告。那么我们只需要在调用Add函数之前对其进行声明就可以消除这一警告,改进如下:

#include <stdio.h>

int Add(int x, int y);//这就是Add函数的函数声明
                      //声明函数只要交代清楚:函数名,函数的返回类型和函数的参数即可
int main()            //而且函数声明中参数只保留类型,省略掉名字也是可以的,如Add函数的声明也可以写成:int Add(int, int);
的
{
	int a = 4;
	int b = 6;
	int r = Add(a, b);
	printf("%d\n", r);
	return 0;
}

int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}

在这里插入图片描述

五、传值调用和传址调用

写⼀个函数,交换两个整数的值。
示例1(传值调用):

#include <stdio.h>

void Swap1(int x, int y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 4;
	int b = 6;
	printf("交换前:a=%d b=%d\n", a, b);
	Swap1(a, b);
	printf("交换后:a=%d b=%d\n", a, b);
	return 0;
}

在这里插入图片描述
我们发现其实没有产生交换的效果,这是为什么呢?
调试以上代码,我们发现:
在这里插入图片描述

我们发现在main函数内部,创建了a和b,a的地址是0x0117fc14,b的地址是0x0117fc08,在调用Swap1函数时,将a和b传递给了Swap1函数,在Swap1函数内部创建了形参x和y接收a和b的值,但是x的地址是0x0117fb30,y的地址是0x0117fb34,x和y确实接收到了a和b的值,不过x的地址和a的地址不⼀样,y的地址和b的地址不⼀样,相当于x和y是独立的空间,那么在Swap1函数内部交换x和y的值,自然不会影响a和b,当Swap1函数调用结束后回到main函数,a和b的没法交换。Swap1函数在使用的时候,是把变量本身直接传递给了函数,这种调用函数的方式叫:传值调用
结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。

所以Swap1是失败的了,它无法交换main函数中两个整数的值。那么该如何进行改进呢?
我们现在要解决的就是当调用Swap函数的时候,Swap函数内部操作的就是main函数中的a和b,直接将a和b的值交换了。那么就需要使用到指针了,在main函数中将a和b的地址传递给Swap函数,Swap函数里边通过地址间接的操作main函数中的a和b,并达到交换的效果就好了。

示例2(传址调用):

#include <stdio.h>

void Swap2(int* px, int* py)
{
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}

int main()
{
	int a = 4;
	int b = 6;
	printf("交换前:a=%d b=%d\n", a, b);
	Swap2(&a, &b);
	printf("交换后:a=%d b=%d\n", a, b);
	return 0;
}

在这里插入图片描述
我们可以看到实现成Swap2的方式,顺利交换了main函数中两个整数的值。这里调用Swap2函数的时候是将变量的地址传递给了函数,这种函数调用方式叫:传址调用

传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用。如果函数内部要修改主调函数中的变量的值,就需要传址调用。

六、嵌套调用和链式访问

1.嵌套调用

函数的嵌套调用就是在调用一个函数的过程中,又调用另一个函数。

比如:

#include <stdio.h>

void printf_2()
{
	printf("i ");
}

void printf_1()
{
	printf_2();
	printf("love ");
}

int main()
{
	printf_1();
	printf("China\n");
	return 0;
}

在这里插入图片描述
以上这⼀段代码中,main 函数先调用printf_1函数,在调用printf_1函数的过程中,printf_1函数又调用printf_2函数,这就是函数的嵌套调用。

需注意的是:函数是可以嵌套调用,但是函数是不能嵌套定义的,在定义函数时,一个函数内不能再定义另一个函数。

2.链式访问

链式访问就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数的链式访问。

比如:

#include <stdio.h>

int main()
{
	int len = strlen("abcdef");//1.strlen求⼀个字符串的⻓度
	printf("%d\n", len);//2.打印⻓度 
	return 0;
}

前面的代码完成动作写了2条语句,把如果把strlen的返回值直接作为printf函数的参数呢?这样就是一个链式访问的例子了。

#include <stdio.h>

int main()
{
	printf("%d\n", strlen("abcdef"));//链式访问
	return 0;
}

标签:调用,return,函数,int,C语言,详解,printf,库函数
From: https://blog.csdn.net/2302_76713442/article/details/141467928

相关文章

  • 新手专科准大一学习c语言的第10天之strcpy、memset、自定义函数的学习与应用
    strcpystrcpy是C语言标准库中的一个字符串操作函数,用于将源字符串复制到目标字符串中。#include<stdio.h>#include<string.h>intmain(){chararr1[50];//确保目标数组足够大,能够容纳源字符串chararr2[]="helloworld";//源字符串......
  • Google Earth Engine(GEE)——GEE函数编写详解4000字(初学者看过来)
    简介代码编辑器提供了对地球引擎全部功能的访问;但是需要对编码和JavaScript的基础知识有基本的了解。在本练习中,您将继续学习JavaScript语法和一些新的EarthEngine空间数据概念。在本练习中,您将重点关注与EarthEngine中图像集合相关的基本概念和方法。这是让用户编......
  • Python——生成器、递归、内省、高阶和偏函数
    Python的生成器(Generators)是一种特殊的迭代器,它使用类似于函数的语法定义,但是使用yield语句一次返回一个值(可以多次返回),而不是使用return语句。生成器函数允许你声明一个像迭代器那样的对象,但是你可以使用更简洁的语法来创建它们。为什么要使用生成器?内存效率高:生成器按需产......
  • 生成函数
    生成函数普通生成函数(ordinarygeneratingfunction,OGF)定义序列\(a\)的普通生成函数为:\[F(x)=\sum_na_nx^n\]\(a\)既可以是有穷序列,也可以是无穷序列。例子:1、序列\(a=\langle1,2,3\rangle\)的OGF为\(1+2x+3x^2\);2、序列\(a=\langle1,1,1,\cdots\rang......
  • [我的C语言学习笔记(08)]C语言输入输出以及缓冲区概念
    查阅stdio.h标准库(https://cplusplus.com/reference/cstdio/),可以发现不少输入输出函数。这些是格式输入输出:这些是字符(包括字符串,也即字符数组)输入输出:这篇会介绍几个常用函数的用法,同时介绍缓冲区的概念。文章目录stream的概念输出printf函数putchar函数pu......
  • 微控制器的功能详解!!!
    微控制器是遥控器的核心组件之一,它负责处理用户通过遥控器输入的指令,并将这些指令转化为无人机能够理解的信号,从而实现对无人机的远程控制。一、微控制器的功能指令接收与解析:微控制器首先接收用户通过遥控器上的按钮、摇杆等输入设备产生的指令信号,然后对这些信号进行解析,提......
  • 【C语言】宏定义详解---老公出轨版 (づ◡﹏◡)づ
    目录C语言宏定义详解1.宏定义关键词总览2.`#define`3.`#undef`4.`#ifdef`5.`#ifndef`6.`#if`7.`#else`8.`#elif`9.`#endif`10.`#include`11.`#error`12.`#pragma`12.1`#pragmaonce`12.2`#pragmapack`12.3`#pragmawarning`12.4`#pragmaGCC`13.`#li......
  • 什么是友元?什么可以做友元?友元能干什么?(全局函数做友元,类做友元,成员函数做友元)c/c++
    一、什么是友元例如:你的生活中有一个特别好的朋友,你允许它进入你的房间(私有场所)也允许他进入客厅(相对公有场所),但是对于其他人你是不会允许他进入你的房间的,只允许他进入客厅。类对象也有这样类似的好朋友类,可以访问本类的私有成员,这个好朋友类就叫做这个类的友元,友元也可......
  • MySQL常用的分组聚合函数
    一、聚合函数(aggregationfunction)---也就是组函数在一个行的集合(一组行)上进行操作,对每个组给一个结果。常用的组函数:AVG([distinct]expr)求平均值COUNT({*|[distinct]}expr)统计行的数量MAX([distinct]expr)求最大值MIN([distinct]exp......
  • Python分布式任务处理库之dramatiq使用详解
    概要在现代Web应用和数据处理任务中,异步任务处理是一个至关重要的部分。Dramatiq是一个用于分布式任务处理的Python库,旨在提供简单、可靠的任务队列解决方案。与其他任务队列库相比,Dramatiq更加轻量级,且易于上手。它的设计目标是帮助开发者轻松地将耗时的任务放到后台......