函数

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

函数分为自定义函数和库函数。库函数是C语言本身提供给我们的函数,一般是大量的普遍的使用,由C语言统一提供可以大大减轻代码书写的负担,也便于代码的可读性。自定义函数是我们想实现不同的功能模块的时候自己定义的函数,不具有统一性。

需要掌握一些基本的库函数的使用,要学会查看库函数的文档,链接:

 cplusplus.com/reference/cstdio/printf/

 std::basic_string - cppreference.com (C语言中文参考手册)

英文还是很重要,不然连参考手册都看不明白

函数的组成:

ret_type fun_name(para1,*)
{
	statement;//语句项
}
  • 1.
  • 2.
  • 3.
  • 4.

其中:ret_type是返回类型,fun_name是函数名, para1是函数参数

statement交代了函数的实现

我们尝试封装一个Swap函数,它的作用是交换两个整型变量的值,但是在实际操作的过程中发现这么编写不能达到目的:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

void Swap(int x,int y)
{
	int temp = 0;
	temp = x;
	x = y;
	y = temp;
}

int main()
{
	int a = 10;
	int b = 20;
	printf("a = %d b = %d\n",a,b);
	Swap(a,b);
	printf("a = %d b = %d\n",a,b);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

分析原因,虽然我们在函数内部对传入的两个值进行了交换,但是我们实际改变的是x和y的值,他们和ab并不是存储在同一位置,因此ab的值并没有得到交换。实际运行结果如下:

8.4C语言学习日志_函数

解决方法是使用指针,我们重新写一个函数Swap2,传入a和b的地址作为指针变量,这样我们在函数内部对两个指针指向的值进行交换就等于对ab的值进行了交换:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

void Swap2(int* pa, int* pb)
{
	int temp = 0;
	temp = *pa;
	*pa = *pb;
	*pb = temp;
}

int main()
{
	int a = 10;
	int b = 20;
	printf("a = %d b = %d\n",a,b);
	Swap2(&a,&b);
	printf("a = %d b = %d\n",a,b);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

运行结果就对了:

8.4C语言学习日志_函数_02


函数的参数


实际参数(实参)

真实传递给函数的参数,叫实参,实参可以是:常量、变量、表达式

函数等。无论实参是何种类型的量,在进行函数调用时,他们都必须由确定的值,以便把这些值传送给形参。


形式参数(形参)

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

例如我们上面实现的交换两个变量的值,第一个写法失败的原因:当实参传递给形参的时候,形参实际上时实参的一份临时拷贝,对形参的修改时不会改变实参的。


函数的调用


传值调用

我们上例中的Swap函数就是传值调用,将ab的值传递到函数中。

传址调用

在上例的Swap函数用的就是传址调用,因为此时我们需要在自定义函数中操作主函数中的变量,将ab的地址作为参数传到函数中。


练习

  1. 写一个函数可以判断一个数是不是素数,调用函数输出100-200之间的素数.
  2. 写一个函数判断一年是不是闰年,打印1000-2000之间的闰年
  3. 写一个函数,实现一个整型有序数组的二分查找
  4. 写一个函数,每调用一次这个函数,就会将num的值增加1

第一题:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

//is_prime函数,若传入的参数为素数返回1,不是素数返回0
int is_prime(int n)
{
	int j = 0;
	int flag = 0;
	for(j=(n-1);j>1;j--)
	{
		if(n%j == 0)
		{
			flag = 0;
			break;
		}
	}
	if(j == 1)
	{
		flag = 1;
	}
	return flag;
}

int main()
{
	int i = 0;
	for(i=100;i <= 200;i++)
	{
		if(is_prime(i) == 1)
		{
			printf("%d ",i);
		}
	}
	return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.

运行结果:

8.4C语言学习日志_指针_03

我在做这道题的时候想在循环和判断中返回值,结果出现了一个警告:

警告	1	warning C4715: “is_prime”: 不是所有的控件路径都返回值	d:\learn\c_learning\test_8_4\test_8_4\test.c	21	1	test_8_4
  • 1.

查了一下,如果要在if分支中return值的话,那么每一个分支都应该给一个返回值,但是这里具体怎么改我没有头绪,用了一个flag变量记录结果最后统一 return。

补:或许可以这样改:

int is_prime(int n)
{
	int j = 0;
	int flag = 0;
	for(j=(n-1);j>1;j--)
	{
		if(n%j == 0)
		{
			return 0;//一旦return了一个值这个函数就终止了
		}
	}
	return 1;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

第二题:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int is_leap_year(int x)
{
	if((x%4==0)&&(x%100!=0) || x%400==0)
	{
		return 1;
	}else
	{
		return 0;
	}
}

int main()
{
	int year = 0;
	for(year=1000;year<=2000;year++)
	{
		if(is_leap_year(year) == 1)
		{
			printf("%d ",year);
		}
	}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

运行结果:

8.4C语言学习日志_函数_04

思考:函数在定义的时候要单一干净,具有可复用性,不要乱返回或者打印输出一些东西。

第三题:

写出bug了:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>


int binary_search(int arr[],int x)
{
	int sz = sizeof(arr)/sizeof(arr[0]);
	int left =  0; 
	int right = sz-1;
	while(left <= right)
	{
		int mid = (left + right)/2;
		if(arr[mid] > x)
		{
			right = mid-1;
		}else if(arr[mid] < x)
		{
			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 ret = 0; 
	printf("输入你要查找的数字:");
	scanf("%d",&k);
	ret = binary_search(arr,k);
	if(ret == -1)
	{
		printf("找不到你要找的值\n");
	}else
	{
		printf("目标值的下标为%d\n",ret);
	}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.

运行结果是错误的:

8.4C语言学习日志_函数_05

错的原因是函数传值的时候我们传入了数组arr[],我误以为传到binary_search函数中的是整个数组,其实不是,我们这里传进去的只有数组第一个元素的地址,这么设计是为了降低存储空间的浪费。因此int binary_search(int arr[],int x)里面的这个arr[]其实是指针,从而我计算数组长度的方式就错了。

解决方法,我们先在主函数中求出数组元素个数,再传给函数就不会出错了。

这样一改就对了:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int binary_search(int arr[],int x,int sz)
{
	int left =  0; 
	int right = sz-1;
	while(left <= right)
	{
		int mid = (left + right)/2;
		if(arr[mid] > x)
		{
			right = mid-1;
		}else if(arr[mid] < x)
		{
			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 ret = 0; 
	int sz = sizeof(arr)/sizeof(arr[0]);
	printf("输入你要查找的数字:");
	scanf("%d",&k);
	ret = binary_search(arr,k,sz);
	if(ret == -1)
	{
		printf("找不到你要找的值\n");
	}else
	{
		printf("目标值的下标为%d\n",ret);
	}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.

运行结果:

8.4C语言学习日志_函数_06


第四题:

比较简单,唯一需要注意的是++的优先级要大于解引用操作符*,因此我们需要给*p用()括起来:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

void Add(int* p)
{
	(*p)++;
}

int main()
{
	int num = 0;
	Add(&num);
	printf("%d\n",num);
	Add(&num);
	printf("%d\n",num);
	Add(&num);
	printf("%d\n",num);
	Add(&num);
	printf("%d\n",num);
	Add(&num);
	printf("%d\n",num);
	return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

运行结果:

8.4C语言学习日志_传参_07