【C语言修炼手册】(三)函数及其相关知识

c语言中函数分类

  1. 库函数
  2. 自定义函数

库函数

将被频繁使用的功能定义出来,让C语言更加标准
所有函数都寄存在头文件里,所以使用库函数时要引用头文件

常见库函数有:

  • IO函数 (putchar scanf getchar…)
  • 字符串操作函数 (strcmp strlen…)
  • 字符操作函数 (toupper…))
  • 内存操作函数 (memcpy memset…)
  • 时间/日期操作函数 (time…)
  • 数学函数(sqrt…)
  • 其他库函数

学习几个库函数(学会查文档非常重要):

  • strcpy----string copy----字符串拷贝
  • strlen----string length----字符串长度有关(包含\0)
  • memset----内存 set 设置

memset在这里插入图片描述

int main()
{
	char arr[] = "hello world";
	memset(arr,'*',5);
	printf("%s\n",arr);    //***** world
	return 0;
}

结果为:
在这里插入图片描述

切记:使用库函数时,必须包含#include对应的头文件

自定义函数

函数的组成

与库函数一样,有函数名,返回值和函数参数,不同的是,我们可以自己定义,也有了更多的可能。
在这里插入图片描述

比较两个值

int get_max(int x, int y)
{
	int z = 0;
	if (x > y)
		z = x;
	else
		z = y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int max = get_max(a, b);

	printf("max=%d\n", max);
	return 0;
}

交换两个值

大家觉得下面这种写法是正确的吗?

函数返回类型的地方写:
void----表示这个函数不返回任何值,也不需要返回
如果什么都不写,则会默认返回 int类型

//第一种方法(传值调用)
void Swap1(int x, int y)
{
	int z = 0;
	z = x;
	x = y;
	y = z;
}
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a= %d b = %d\n", a, b);
	Swap1(a, b);
	printf("交换后:a= %d b = %d", a, b);
	return 0;
}

在这里插入图片描述

很多初学者可能第一反应会觉得这种写法是正确的,然鹅。事实却是残酷的=-=这是为什么呢?从下面的图片我们可以知道原因

在这里插入图片描述
在这里插入图片描述

接下来我们就来了解一下指针

int main()
{
	int a = 10;
	int* pa = &a; //pa就是一个指针变量
	*pa = 20;  //*pa即对pa解引用-通过pa找到 a
	printf("%d", a);
	return 0;
}

我们可以利用指针对第一种方法改造一下

//第二种方法(传址调用)
void Swap1(int* pa,int* pb)
{
	int tmp = 0;
	tmp = *pa;
	*pa = tmp;
	//return;
	//void不需要返回,没有必要
}
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a= %d b = %d\n", a, b);
	Swap1(&a, &b);
	printf("交换后:a= %d b = %d", a, b);
	return 0;
}

通过指针,我们就能让函数之间建立联系

函数的参数

在这里插入图片描述

  • 实参

实参可以是:常量,变量,表达式,函数等。在进行函数调用时,必须有确定的值。

  • 形参

完成函数调用后自动销毁。(类似局部变量)----形参只在函数中有效

在这里插入图片描述

swap1在被调用的时候,实参传给形参,其实形参实例化后就等同与被临时拷贝。
改变形参,不能改变实参,但是为什么swap1可以改变呢?,其实就是因为形参和实参之间建立了联系

函数的调用

传值调用

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

传址调用

即上例子中所用
这种传参方式可以让函数和函数外边的变量建立起真正的联系,让函数内部可以直接操作函数外部的变量

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

函数不能嵌套定义

嵌套调用
void test3()
{
	printf("hehe\n");
}

int test2()
{
	test3();
	return 0;
}

int main()
{
	test2();
	return 0;
}
链式访问

返回值作参数

#include<string.h>
int main()
{
	//int len = strlen("abc");
	//printf("%d\n", len);

	printf("%d\n", strlen("abc"));
}
考考你

曾经有这样一道题

printf("%d", printf("%d", printf("%d", 43)));

你们觉得这道题结果是多少呢?
在这里插入图片描述

你们有没有做对呢?下面来解释一下原因
在这里插入图片描述

考考你

我们不能光说不做,下面一起来做一些练习吧

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

#include<math.h>
int is_prine(int n)
{
	int j = 0;
	for (j = 2; j <= sqrt(n); j++)
	{
		if (n % j == 0)
			return 0;
	}
	return 1;
}
int main()
{
	int i = 0;
	int count = 0;
	for (i = 100; i <= 200; i++)
	{
		if (is_prine(i) == 1)
		{
			printf("%d是素数 \n", i);
			count++;
		}
	}
	printf("%d ", count);
	return 0;
}

注意:在写函数时我们要明确实际需求,不要写多余的语句

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

int is_leap_year(int n)
{
	//if (n % 4 == 0 && n % 100 != 0 || n % 400 == 0)
	//{
	//	return 1;
	//}
	//else
	//	return 0;
	//我们也可以写成下面这样
	return (n % 4 == 0 && n % 100 != 0 || n % 400 == 0);
}
int main()
{
	int y = 0;
	for (y = 1000; y <= 2000; y++)
	{
		if (is_leap_year(y) == 1)
		{
			printf("%d ",y);
		}
	}
}

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

int binary_search(int a[], int k, int s)
{
	int left;
	int right = s - 1;

	while (left <= right)
	{
		int mid = (left + right) / 2;
		if (a[mid] > k)
		{
			right = mid - 1;
		}
		else if (a[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 key = 7;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//找不到返回-1(不与数组下标冲突)

	int ret = binary_search(arr,key,sz);
	if (-1 == ret)
	{
		printf("找不到\n");
	}
	else
	{
		printf("找到了,下标是:%d\n", ret);
	}
	return 0;
}

在这里插入图片描述

有的同学可能会想能不能把这里的sz给删掉,在自定义函数内部求sz在这里插入图片描述

在这里插入图片描述

求得的sz怎么会是1呢?
数组arr传参,实际传递的不是数组的本身
仅仅传过去了数组首元素的地址(实际上也是一种传址)
在这里插入图片描述
因此,我们不能在自定义函数内部求sz大小
写一个函数,每调用一次函数,会将num的值加1

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);
	return 0;
}

通过这道题,相信大家对什么时候传值,什么时候传址有了自己的体会

函数的声明和定义

函数声明:
1.告诉编译器有一个函数叫什么,参数是什么,返回类型是什么
2.一般出现在函数使用前面。切记先声明后使用
3.一般放在头文件中

函数定义:
1函数定义是指函数的具体实现,交代函数的功能实现

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值