广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言函数超详细讲解上篇
  • 679
分享到

C语言函数超详细讲解上篇

2024-04-02 19:04:59 679人浏览 八月长安
摘要

目录前言1、函数是什么?2、C语言中函数的分类2.1 库函数2.1.1 如何学会使用库函数2.1.2 自定义函数3、函数的参数3.1 实际参数(实参)3.2 形式参数(形参)4、函数

前言

本文主要学习函数的相关内容。

1、函数是什么?

维基百科中对函数的定义:子程序

  • 在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
  • 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。

2、C语言中函数的分类

  • 库函数
  • 自定义函数

2.1 库函数

  • 在学习C语言编程时,总是在一个代码编写完成之后,想把这个结果打印到屏幕上看看。这时使用一个功能:将信息按照一定的格式打印到屏幕上(printf)。
  • 在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)
  • 在编程是我们也计算,总是会计算n的k次方这样的运算(pow)

上面的函数不用自己编写,直接可以调用。为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发

C语言常用的库函数都有:

  • IO函数
  • 字符串操作函数
  • 字符操作函数
  • 内存操作函数
  • 时间/日期函数
  • 数学函数
  • 其他库函数

使用库函数,必须包含 #include 对应的头文件。

2.1.1 如何学会使用库函数

推荐查询工具官网:

MSDN(Microsoft Developer Network)

www.cplusplus.com

Http://en.cppreference.com(英文版)

http://zh.cppreference.com(中文版)

2.1.2 自定义函数

自定义函数和库函数一样,有函数名,返回值类型和函数参数。这些都是我们自己来设计,函数的组成:

ret_type fun_name(para1, * )
{
	statement;//语句项
}
//ret_type 返回类型
//fun_name 函数名
//para1 函数参数
//举例:写一个函数可以找出两个整数中的最大值
//get_max函数的设计
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;
}

3、函数的参数

3.1 实际参数(实参)

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

3.2 形式参数(形参)

  • 形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数
  • 形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效
//举例2:写一个函数可以交换两个整形变量的内容
//实现成函数,但是不能完成任务
int exchange1(int x, int y)
{//当实参传给形参时候,形参是实参的一份临时拷贝,
	//对形参的修改不会影响实参
	int temp = x;
	x = y;
	y = temp;
}
//正确的版本
int exchange2(int* pa, int* pb)//定义指针,接收地址
{
	int temp = *pa;
	*pa = *pb;
	*pb = temp;
}
int main()
{
	int a = 3;
	int b = 5;
	exchange1(a, b);//传参是值
	printf("exchange1::a = %d b = %d\n", a, b);//交换前

	exchange2(&a, &b);//传参是地址
	printf("exchange2::a = %d b = %d\n", a, b);//交换后的
	//传入地址,自定义的形参和实参联系更加紧密,能改变地址存储的数值
	//此时,形参的地址与实参的地址是一样的
	return 0;
}

上面代码中, exchange1和 exchange2函数中的参数 x,y,pa,pb 都是形式参数。在main函数中传给 exchange1的 a ,b 和传给 exchange2函数的 &a ,&b是实际参数。

运行结果如下所示,exchange1并没有起到预想的交换数值的作用,exchange2可以。

在这里插入图片描述

在这里插入图片描述

通过监视变量发现:

名称意义
a5数值为5
&a0x113f8e4数值a的地址
x5形参x数值为5
&x0x113f800形参x的地址
pa0x113f8e4形参pa数值为a的地址
  • 变量a在内存中开辟了空间,地址是0x113f8e4
  • 函数exchange1将实参a传递给形参x,x的数值也是5,但此时形参x另外重新再内存中开辟了空间,地址 0x113f800,形参和实参的地址是不一样的
  • 函数exchange2将实参a传递给形参pa,pa的值是变量a的地址0x113f8e4,这个地址里面存放变量a的数值5。

在这里插入图片描述

这里可以看到 exchange1函数在调用的时候, x , y 拥有自己的空间,同时拥有了和实参一模一样的内容。所以我们可以简单的认为:

形参实例化之后其实相当于实参的一份临时拷贝。形参x、y的值发生交换,但是不影响实参a、b的值。一般性的只是使用数值大小,利用形参传值就可以了,传值表明,形参和实参的关系肤浅,仅限于表面数值的拷贝。

如果需要对主函数的实参值进行操作,比如交换,此时形参需传地址,功能更为强大。传地址表明,形参和实参的关系更深一步,直接可以通过地址修改实参的数值。

4、函数的调用

4.1 传值调用

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

4.2 传址调用

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

4.3 练习

4.3.1 判断一个数是不是素数

写一个函数可以判断一个数是不是素数

//返回1 表示是素数
//返回-1 表示不是素数

//写法1  2-n-1 试除法
int  is_sushu(int a)
{//素数就是除1和自身外,不能被其他数整除
	for (int i = 2; i < a-1; i++)
	{//用2-n-1的数一一试除
		if (a%i==0)
		{//若有能除的,直接返回-1,跳出后面的循环了
			return -1;//表明不是素数,
		}
	}//break退出循环会调到这里
	//return返回时直接退出这个函数了,返回到主函数了,级别更高
	return 1;
}

int main()
{
	int a = 0;
	scanf("%d", &a);
	int num = is_sushu(a);
	if (num==1)
	{
		printf("%d: 是素数", a);
	}
	else
	{
		printf("%d:不是素数", a);
	}
	return 0;
}

//写法2  2-sqrt(n) 试除法,速度更快
#include<math.h>
int  is_sushu(int num)
{
	for (int i = 2; i <=sqrt(num); i++)
	{
		if (num%i == 0)
		{
			return -1;
		}
	}
	return 1;
}

int main()
{
	int num = 0;
	for (int num = 100; num < 201; num++)
	{
		if ((is_sushu(num)) == 1)
		{
			printf("%d ", num);
		}
	}
	return 0;
}

4.3.2 判断一年是不是闰年

写一个函数判断一年是不是闰年

//闰年时是4的倍数且不是100的倍数,或者是400的倍数
int  is_run_nian(int y)
{
		if (((num%4 == 0)&& (num%100 != 0))|| (num % 400 == 0))
		{
			return 1;
		}
	//return ((num%4 == 0)&& (num%100 != 0))|| (num % 400 == 0);//简写
		
}
int main()
{
	int num = 0;
	for (int num = 1000; num <= 2000; num++)
	{
		if ((is_run_nian(num)) == 1)
		{
			printf("%d ", num);
		}
	}
	return 0;
}

4.3.3 二分查找

写一个函数,实现一个整形有序数组的二分查找

//找到了就返回下标
//找不到返回-1
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int k = 0;
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int left = 0;//左下标
	int right = sz - 1;//右下标

	scanf("%d", &k);
	while (left<=right)
	{//每次left right mid都要更新
		int mid = left + (right - left) / 2;		   
		if (arr[mid] > k)
		{//寻找的数值在左半边,所以左不动,右边动
			right = mid - 1;
		}
		else if (arr[mid] < k)
		{//寻找的数值在右半边,所以左边动,右不动
			left = mid + 1;
		}
		else 
		{
			printf("找到了:%d ", mid);
			break;
		}
	}
	if (left>right)//循环结束了
	{
		printf("找不到");
	}
	return 0;
}

可以将二分法进行改进,写成独立的函数,让函数功能单一化:

int is_erfen(int arr[],int k, int sz)
{//数组传递参数进来就是数组首元素的地址,并不是整个数组
	//地址的大小是4或8个字节,前面有讲到过的
//x86平台中,地址占4个字节,sizeof(arr)是4, 而不是40了
	//int sz = sizeof(arr) / sizeof(arr[0]);//sz要在主函数计算,
	int left = 0;
	int right = sz - 1;
	while (left <= right)
	{//每次left right mid都要更新
		int mid = left + (right - left) / 2;
		if (arr[mid] > k)
		{
			right = mid - 1;
		}
		else if (arr[mid] < k)
		{
			left = mid + 1;
		}
		else
		{
			return mid;
		}
	}
	return -1;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int k = 0;
	int i = 0;
	scanf("%d", &k);
	//主函数中  按数组名找到整个数组,求出占用字节是40
	int sz = sizeof(arr) / sizeof(arr[0]);
	int num = is_erfen(arr, k, sz);

	if (num ==-1)
	{
		printf("找不到"); 
	}
	else
	{
		printf("找到了:%d ", num);
	}
	return 0;
}

在这里插入图片描述

在上面的函数中需要注意,数组的大小必须在主函数中计算,下面代码可说明在主函数中和其他函数中求数组长度的区别:

int erfen(int arr[])
{//数组传递进来,只能是首地址,这个arr数组里只有{1},长度为4
	int sz1 = sizeof(arr) / sizeof(arr[0]);,//4/4 = 1求出只有1个元素
	printf("erfen求数组大小,sz1:%d\n ", sz1);
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };	
	//数组长度为40
	int sz = sizeof(arr) / sizeof(arr[0]);//40/4 = 10 求出数组10个元素
	printf("main中求数组大小,sz:%d\n ", sz);
	erfen(arr);	
	return 0;
}

在这里插入图片描述

按F10进入调试界面,按F11,可观察到:

  • 主函数中sizeof(arr)长度是40,数组中包含10个元素。
  • 而在函数erfen中,sizeof(arr)长度是4,数组中包含1个元素,就是首元素{ 1 }。

在这里插入图片描述

由此,可知道,参数里传递数组时,实际传递的数组就是数组的地址,也是数组首元素的地址。数组名传递进来,只能是首地址,这个arr数组里只有首元素{1},长度为4。整个数组是传递不了的。因此,必须在主函数里求取数组的长度。

因为传递的参数是地址,所以erfen中也可以定义指针来接受数组:

//int erfen(int arr[])//接受数组,只能接受一个首元素
int erfen(int* arr)
{//数组传递进来,只能是首地址,这个arr数组里只有{1},长度为4
	int sz1 = sizeof(arr) / sizeof(arr[0]);,//4/4 = 1求出只有1个元素
	printf("erfen求数组大小,sz1:%d\n ", sz1);
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };	
	//数组长度为40
	int sz = sizeof(arr) / sizeof(arr[0]);//40/4 = 10 求出数组10个元素
	printf("main中求数组大小,sz:%d\n ", sz);
	erfen(arr);	
	return 0;
}

运行结果一样

在这里插入图片描述

4.3.4 数值自增增加1

写一个函数用一次这个函数,就会将 num 的值增加1

int add(int* p)
{
	(*p)++;//参数传地址,可以操作实参的值
}
int main()
{
	int a = 10;
	add(&a);//传地址
	printf("%d\n", a);
	add(&a);
	printf("%d\n", a);
	add(&a);
	printf("%d\n", a);
	add(&a);
	printf("%d\n", a);
	return 0;
}

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

函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。

5.1 嵌套调用

//套娃
void newline()
{
	printf("hehe\n");
}
void threeline()
{
	int i = 0;
	for (int i = 0; i < 3; i++)
	{
		newline();
	}
}
int main()
{
	threeline();
	newline;
	return 0;
}

5.2 链式访问

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

int main()
{
	char arr[20] = "hello";
	int ret = strlen(strcat(arr, "bit"));
	printf("%d\n", ret);
	return 0;
}
int main()
{	
	printf("%d", printf("%d", printf("%d", 43)));//输出4321,
	//前面是打印43 2是返回两个字符(因为4 、3是两个字符)  
	//1是返回1个字符(2是1个字符)

	return 0;
}

输出结果见下图:

在这里插入图片描述

在这里插入图片描述

总结

本文主要介绍了函数相关的知识,下一篇接着介绍函数的内容。

到此这篇关于C语言函数超详细讲解上篇的文章就介绍到这了,更多相关C语言 函数内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C语言函数超详细讲解上篇

本文链接: https://www.lsjlt.com/news/145909.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C语言函数超详细讲解上篇
    目录前言1、函数是什么?2、C语言中函数的分类2.1 库函数2.1.1 如何学会使用库函数2.1.2 自定义函数3、函数的参数3.1 实际参数(实参)3.2 形式参数(形参)4、函数...
    99+
    2022-11-13
  • C语言函数超详细讲解下篇
    目录前言函数的声明和定义函数声明函数定义举例简单的求和函数把加法单独改写成函数添加函数声明带头文件和函数声明静态库(.lib)的生成静态库文件的使用方法函数递归什么是递归?递归的两个...
    99+
    2022-11-13
  • C语言指针超详细讲解上篇
    目录前言1、指针是什么1.1 指针变量1.2 指针是内存中一个最小单元的编号2、指针和指针类型2.1 指针±类型2.2 指针的解引用2.2.1 int* 类型的解引用2...
    99+
    2022-11-13
  • C语言操作符超详细讲解上篇
    目录前言1、操作符的分类2、算术操作符3、移位操作符3.1 左移操作符3.1.1 正数左移1位3.1.2 负数左移1位3.2 右移操作符3.2.1 正数右移1位3.2.2 负数右移1...
    99+
    2022-11-13
  • C语言数据的存储超详细讲解上篇
    目录前言1、数据类型介绍类型的基本归类2、整形在内存中的存储2.1 原码、反码、补码2.2 大小端介绍2.2.1 什么是大小端2.2.2 大端和小端意义2.2.3 写程序判断字节序总...
    99+
    2022-11-13
  • C语言超详细讲解排序算法上篇
    目录1、直接插入排序2、希尔排序(缩小增量排序)3、直接选择排序4、堆排序进入正式内容之前,我们先了解下初阶常见的排序分类 :我们今天讲前四个! 1、直接插入排序 基本思...
    99+
    2022-11-13
  • C语言超详细讲解库函数
    目录1 返回整数的getchar函数2 更新顺序文件3 缓冲输出与内存分配4 库函数练习1 返回整数的getchar函数 代码: #include<stdio.h> ...
    99+
    2022-11-13
  • C语言数组超详细讲解上
    目录前言1、一维数组的创建和初始化1.1 一维数组的创建1.2 一维数组的初始化1.3 一维数组的使用1.4 一维数组在内存中的存储2、二维数组的创建和初始化2.1 二维数组的创建2...
    99+
    2022-11-13
  • C语言指针超详细讲解下篇
    目录前言指针运算指针±整数指针-指针指针的关系运算指针和数组二级指针指针数组举例 1举例 2总结前言 本文接着上一篇内容,继续学习指针相关知识点。 指针运算 指针&pl...
    99+
    2022-11-13
  • C语言数组超详细讲解下篇扫雷
    目录前言1、扫雷是什么?2、程序框架2.1 主函数2.2 函数menu2.3 函数game2.3.1 函数init_board2.3.2 函数show_board2.3.3 函数se...
    99+
    2022-11-13
  • C语言操作符超详细讲解下篇
    目录前言赋值操作符单目操作符单目操作符介绍sizeof 和 数组关系操作符逻辑操作符条件操作符逗号表达式下标引用与函数调用和结构成员[ ] 下标引用操作符( ) 函数调用操作符访问一...
    99+
    2022-11-13
  • C语言数组超详细讲解中篇三子棋
    目录前言1、三子棋是什么?1.1 百度百科1.2 游戏编程准备工作2. 程序实现2.1 搭建程序框架2.2 模块化编程2.2.1 源文件test.c2.2.2 源文件play.c2....
    99+
    2022-11-13
  • C语言超详细讲解排序算法下篇
    目录1、冒泡排序2、快速排序 ( 三种方法 )3、归并排序4、排序算法复杂度及稳定性分析 上期学习完了前四个排序,这期我们来学习剩下的三个排序 1、冒泡排序 &n...
    99+
    2022-11-13
  • C语言超详细讲解getchar函数的使用
    目录一、getchar 函数二、缓冲区1、什么是缓冲区2、为什么要存在缓冲区3、缓冲区的类型4、缓冲区的刷新三、getchar 函数的正确使用1、getchar 的换行问题2、get...
    99+
    2022-11-13
  • C语言超详细讲解函数指针的运用
    目录前言计算器的例子回调函数转移表前言 前面我们学习了各种各样的指针类型,有些指针可以说是稀奇百怪,特别是函数指针,有些朋友可能觉得,函数指针有些多余,调用函数为什么要用指针调用,直...
    99+
    2022-11-13
  • C语言超详细讲解指向函数的指针
    目录一、函数的指针二、指向函数的指针变量三、调用函数的两种方式四、指向函数的指针的作用五、用指向函数的指针作函数参数(重点)六、为什么要将指向函数的指针变量作为函数的形参(重点)一、...
    99+
    2022-11-13
  • C语言数据的存储超详细讲解中篇练习
    目录前言数据的存储的知识点练习练习 1练习 2练习 3练习 4练习 5练习 6练习 7总结前言 本文继续学习数据在内存中存储的相关知识点。 数据存储整型提升 数据的存储的知识点练习 ...
    99+
    2022-11-13
  • C语言超细致讲解函数递归
    目录前言什么是递归递归的两个必要条件题解递归递归与迭代练习题结束语前言 最近被函数递归困恼许久,今天就带领大家一起探秘递归。 什么是递归 程序调用自身的编程技巧称为递归( recur...
    99+
    2022-11-13
  • C语言超详细讲解轮转数组
    目录题目描述实例解题思路1. 先整体逆转2.逆转子数组[0, k - 1]3.逆转子数组[k, numsSize - 1]易错点代码题目描述 给你一个数组,将数组中的元素向右轮转 k...
    99+
    2022-11-13
  • C语言超详细讲解字符串函数和内存函数
    目录字符串函数长度不受限制的字符串函数strlenstrcpystrcatstrcmp长度受限制的字符串函数介绍strncpystrncatstrncmp字符串查找以及错误报告str...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作