函数相关知识

我们已经学习了分支和循环语句的知识,今天我们将学习新的一个章节——函数,我们在初识C语言中曾写过一个函数用来求得两个整型变量的最大值,那么今天我们来研究一下函数到底是什么以及函数是怎么执行的。

目录:

1. 函数是什么
2. 库函数
3. 自定义函数
4. 函数参数
5. 函数调用
6. 函数的嵌套调用和链式访问
7. 函数的声明和定义
8. 函数递归(函数递归我们将另外写一篇博客细说)

一·函数 

1.1 函数是什么?

我们都知道,一个程序必须有main函数这个主体,这个main函数就是一个主函数,一个工程中只能有一个main函数,我们在main函数中书写我们所需要的语句项,当我们想在main函数内执行一些功能时,我们就会采用函数封装的方法,所以函数也可以称为子程序,是为了完成所需的功能的代码块。

1.2 C语言函数的分类

C语言中函数一般分为库函数和自定义函数。其中库函数是C语言本身所具有的,我们在书写代码时只需调用即可(调用时需要引用头文件),但是C语言并不能包含所有的函数,因为每个程序员的功能和需求不同,所以自定义函数的书写对于我们来说也尤为重要。

这里我将给大家讲解两个库函数的实现功能:

1.strcpy(字符串复制函数)

函数模型:
char * strcpy ( char * destination , const char * source );

 该函数的功能就是把一个字符串的内容复制给另一个字符串,注意参数的位置,被复制的字符串作为函数的第一个参数

//strcpy函数的实现

int main()
{
	char arr1[10] = "abcdef";
	char arr2[10] = { 0 };
	strcpy(arr2, arr1);
	printf("%s\n", arr2);

	return 0;
}

 2.memset(内存设置函数)

函数模型:

void * memset ( void * ptr , int value , size_t num );

 该函数的功能是把目标数组中第几个字符开始(即指针)向后num个字节设置为value

int main()
{
	char arr1[] = "hello world!";
	char arr2[] = "############";
	memset(arr2, 'X', 5);
	printf("%s\n", arr2);


	return 0;
}

1.3 如何学习库函数?

C语言自带的库函数有很多,我们根据每一个函数模型中的参数可以大概猜测出该库函数所执行的功能以及参数的类型,但是我们如何准确的知道每一个库函数的实现以及示例呢?这里我给大家几个网站去学习库函数相关内容。

1.MSDN(Microsoft Developer Network)可自行去官网下载或者私信我发你安装包

2.www.cplusplus.com

3.http://en.cppreference.com(英文版)

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

1.4 自定义函数 

上面浅浅的学习了一下库函数的使用,下面自定义函数的使用和如何执行才是我们学习的主要目标,否则要我们程序员干嘛呢!

首先我们来看一下自定义函数的组成

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

 实践是检验真理的唯一标准,下面我们来举一个例子来详细说明:写一个函数可以找出两个整数中的最大值。

int get_max(int x, int y)
{
	return ((x > y) ? x : y);
}

int main()
{
	int x = 0;
	int y = 0;
	scanf("%d %d", &x, &y);
	int max = get_max(x, y);

	printf("%d\n", max);

	return 0;
}

 有编译结果可知,我们求得了10 20之间的较大值为20,我们书写一个get_max函数来执行这个功能,其返回类型为int,因为我们在主函数中定义了一个int类型的变量max来接受函数执行之后的返回值

我们再举一个例子:写一个函数可以交换两个整形变量的内容

//实现成函数,但是不能完成任务
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;
}

 这里我写了两个版本Swap1和Swap2函数,其中Swap1函数传递的是变量num1和num2,而Swap2函数传递的是变量num1和num2的地址,为什么前者编译结果错误,后者编译结果正确呢?

原因是:

当我们把变量num的值传递给函数时,将会新建一个变量来接受传递过来的值,但是新建的变量和传递给函数的变量再内存中所占的内存空间并不相同(言下之意就是两个变量只是数值上相等而已),所以对新建变量的修改并不会改变变量num的值,所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝。

二·函数的参数

2.1 实际参数(实参):

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

2.2 形式参数(形参):

形式参数是指函数名后括号中的变量, 因为形式参数只有在函数被调用的过程中才实例化(分配内
存单 元) ,所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有 效。
上面 Swap1 Swap2 函数中的参数 x y px py 都是 形式参数 。在 main 函数中传给 Swap1 num1 ,num2 和传给 Swap2 函数的 &num1 &num2 实际参数

三· 函数的调用

3.1 传值调用

函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参,所以刚才的Swap1函数执行完之后并不会交换两个变量num1和num2的值。

3.2 传址调用

传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量,由此可知在Swap2函数中传递的是变量的地址时,可以交换num1和num2的值。

 3.3 练习

1.打印100-200之间的素数

//1.普通版本
int is_prime(int i)
{
	int j = 0;
	//用2-i-1的数来试除i
	for (j = 2; j < i; j++)
	{
		if (i % j == 0)
		{
			return 0;
		}
	}
	return 1;
}

int main()
{
	int count = 0;//计数
	int i = 0;
	for (i = 100; i <= 200; i++)
	{
		//若为素数返回1否则返回0
		if (is_prime(i))
		{
			printf("%d ", i);
			count++;
		}
	}
	printf("\n count = %d\n", count);

	return 0;
}

//2.优化版本
#include <math.h>
int is_prime(int i)
{
	int j = 0;
	//用2-i-1的数来试除i
	for (j = 2; j <= sqrt(i); j++)
	{
		if (i % j == 0)
		{
			return 0;
		}
	}
	return 1;
}

int main()
{
	int count = 0;//计数
	int i = 0;
	for (i = 100; i <= 200; i++)
	{
		//若为素数返回1否则返回0
		if (is_prime(i))
		{
			printf("%d ", i);
			count++;
		}
	}
	printf("\n count = %d\n", count);

	return 0;
}

 

 其中优化版本使用了sqrt这个函数,读者可自行查阅,需引用头文件math.h

2.打印1000-2000之间的闰年

//闰年
//1.能被4整除但是不能被100整除的数
//2.能被400整除的数
int is_leap_year(int i)
{
	if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

int main()
{
	int count = 0;//计数
	int year = 0;
	for (year = 1000; year <= 2000; year++)
	{
		//若为闰年返回1否则返回0
		if (is_leap_year(year))
		{
			printf("%d ", year);
			count++;
		}
	}
	printf("\n count = %d\n", count);

	return 0;
}

 

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

int Binary_search(int arr[],int k, int sz)
{
	int left = 0;
	int right = sz - 1;
	while (left <= right)
	{
		int mid = left + (right - left) / 2;
		if (arr[mid] < k)
		{
			left = mid + 1;
		}
		else if (arr[mid] > k)
		{
			right = mid - 1;
		}
		else
		{
			return mid;
		}
	}
	return -1;
}

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int k = 7;//假设所需查找的数是7
	int sz = sizeof(arr) / sizeof(arr[0]);

	//若查找成功则返回下标否则返回-1
	int ret = Binary_search(arr,k, sz);

	if (ret != -1)
	{
		printf("下标为:%d\n", ret);
	}
	else
	{
		printf("没找到\n");
	}

	return 0;
}

 其中需要注意的是,数组的大小sz必须在主函数中计算之后传递给查找函数,因为数组传参时传递的是数组首元素的地址并不是整个数组

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

//传址调用
void Add(int* p)
{
	int z = *p;
	(*p)++;
	return z;
}

int main()
{
	int num = 0;
	Add(&num);//因为需要改变num的值,所以采用传址调用
	printf("%d\n", num);
	Add(&num);
	printf("%d\n", num);
	Add(&num);
	printf("%d\n", num);

	return 0;
}

//传值调用
int Add(int num)
{
	return (++num);//注意这里是++num而不是num++
}

int main()
{
	int num = 0;
	num = Add(num);//每次返回的值用num接收
	printf("%d\n", num);
	num = Add(num);
	printf("%d\n", num);
	num = Add(num);
	printf("%d\n", num);

	return 0;
}

 四·函数的嵌套调用和链式访问

4.1 嵌套调用

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;
}

我们在主函数中调用three_line函数 ,在three_line函数中调用new_line函数,在函数中调用函数这个操作我们称为函数的嵌套调用,所以结果打印出来为三个hehe

注意:函数可以嵌套调用,但是不能嵌套定义,因为对于任何一个函数来说 ,地位都是相等的,不能再函数中去定义另一个函数

4.2 链式访问

示例1:

#include <string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));

	return 0;
}

 显然结果是6,strlen是一个计算字符串长度的函数,我们将strlen函数执行的结果放在printf函数中打印出来了,这便是函数的链式访问

示例2:

int main()
{
	printf("%d", printf("%d", printf("%d", 43)));
	//结果是啥?
	//注:printf函数的返回值是打印在屏幕上字符的个数
	return 0;
}

 其中printf函数的返回值是打印在屏幕上字符的个数,那结果读者自行理解

五·函数的声明和定义

5.1 函数的声明

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

5.2 函数的定义

函数的定义是指函数的具体实现,交待函数的功能实现。
具体分文件操作我们后面将在讲解小游戏三子棋和扫雷的时候讲解!!!



函数部分我们只剩下一个函数的递归了,由于函数的递归较难,我将单独写一篇博客进行讲解。希望大家可以今天函数的内容消化吸收,加油,感谢支持!!!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值