目录
1. 函数是什么?
2. C语言中函数的分类:
2.1 库函数:
2.2 自定义函数
函数的基本组成:
3. 函数的参数
3.1 实际参数(实参):
3.2 形式参数(形参):
写一个函数可以交换两个整形变量的内容
错误示范:
正确代码如下:
小结:
4. 函数的调用
4.1 传值调用
4.2 传址调用
4.3 练习
4.3.1 写一个函数可以判断一个数是不是素数
4.3.2 写一个函数判断一年是不是闰年
4.3.3 写一个函数,实现一个整形有序数组的二分查找
4.3.4 写一个函数,每调用一次这个函数,就会将num的值+1
1. 函数是什么?
在数学中函数的概念大家应该都有所了解:y = f(x) ;我们可以代入x求出相应的y。但是,你了解C语言中的函数吗?
- 函数其实就是一部分的代码,它们可能有一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具有相对的独立性。
- 一般有输入参数并会有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
2. C语言中函数的分类:
库函数和自定义函数
2.1 库函数:
为什么会有库函数?
- 我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格式打印到屏幕上。(printf)
- C语言官方了解到我们在编写代码的时候会有一些小动作会频繁大量的使用,如果有一个库函数来存放这些代码,这样我们就可以直接使用。如果没有这个库,那么同一个功能可能会有许多种写法,品质良莠不齐,可读性不高,可移植性不高。
- 在C语言的标准库里提供了一系列C语言的库函数
学习库函数的网站:www.cplusplus.com
这里简单的介绍一下常用的库函数:
- IO函数— — — — I=input; O=output; 输入输出函数:scanf、printf
- 字符串操作函数— — — — strlen、strcmp……
- 内存操作函数— — — — 大小写转换、字符分类
- 时间/日期函数— — — — time
- 数学函数— — — — pow、sqrt
- 其他库函数
2.2 自定义函数
库函数不是万能的,只含有常用的函数,自定义函数更加重要!
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
但是不一样的是自定义函数是由程序员自己设计的。这给程序员提供了一个很大的发挥空间。
函数的基本组成:
ret_type 返回类型
fun-name 函数名
para 1 函数参数 — — 参数可以是0,可以是一个或多个
{ }内为函数体,函数体中包含的是语句项
3. 函数的参数
3.1 实际参数(实参):
- 真实传给函数的参数,叫实参。
- 实参可以是:常量、变量、表达式、函数等。
- 无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值都传给形参。
3.2 形式参数(形参):
形式参数是指函数名后括号中的变量,应为形式参数只有在函数被调用的过程中猜实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成后就自动销毁了。因此,形式参数只在函数中有效。
写一个函数可以交换两个整形变量的内容
1.定义a、b存放并输入整数
2.先在屏幕中显示出原来的两个整形顺序,然后用自定义函数调换,最后在屏幕中显示改变顺序的整形。
3.写自定义函数:采用取地址的方式,将a、b取地址,定义一个tmp存放临时的数字,进行调换,最后取地址回去
错误示范:
void Swap(int x,int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n",a,b);
Swap(a, b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
这里主要的错误就是调用函数部分,我们通过调试可以看到:
虽然我们的数值是给到了x、y,但是x、y的地址和a、b的地址不一样,就导致了即使我们交换了x、y, a、b的数值也没有改变。
因此,我们在写调用函数时不使用x、y,改为取a、b的地址(指针)
正确代码如下:
void Swap(int* pa,int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n",a,b);
Swap(&a, &b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
小结:
实参传递给形参的时候,形参是实参的一份临时拷贝,对形参的修改是不会影响实参的!
4. 函数的调用
4.1 传值调用
- 函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参。
(例如上面的错误示范)
4.2 传址调用
- 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
- 这种传参方式可以让函数和函数外边的变量建立起真正的练习,也就是函数内部可以直接操作函数外部的变量。
(例如上面的正确示范)
4.3 练习
- 本章练习均以上一篇博客为基础忘记了的话可以看一下我前面的文章:C语言基础练习.2.里面有讲具体该如何操作
4.3.1 写一个函数可以判断一个数是不是素数
我们在之前写过一个“打印100-200之间的素数”,这次我们在原来的基础上面改动:
1.首先写主函数部分,定义i=100;使用for循环创建100-200之间的数字
2.用if语句,如果 i 是素数:打印并计数;
3.写调用函数,我在这里是创建is_prime为函数名。又因为我们最后要返回素数,所以返回类型为int
4.定义 j ,用for循环计算 i 是否为素数,如果是就返回1;如果不是则返回0,即不返回。
5.打印素数,并打印有几个素数
代码如下:
int is_prime(int i)
{
int j = 0;
for (j = 2; j <= sqrt(i); j++)
{
if (i % j == 0)
return 0;
}
return 1;
}
int main()
{
int i = 0;
int count = 0;
for (i = 100; i <= 200; i++)
{
if (1 == is_prime(i))
{
count++;
printf("%d ", i);
}
}
printf("\ncount=%d", count);
return 0;
}
4.3.2 写一个函数判断一年是不是闰年
1.首先写主函数定义y=0;使用for循环创建出1000-2000之间的数字
2.用if语句判断是否为闰年,是就打印
3.写调用函数部分,使用if语句判断:是返回1,不是返回0
int is_leap_year(int y)
{
if (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)))
return 1;
else
return 0;
}
int main()
{
int y = 0;
for (y = 1000; y <= 2000; y++)
{
if (1 == is_leap_year(y))
{
printf("%d ", y);
}
}
return 0;
}
在此基础上,我们还能进行改良:
其实((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))这一句如果正确返回值就为1
我们可以直接改为:
int is_leap_year(int y) { return ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)); }
4.3.3 写一个函数,实现一个整形有序数组的二分查找
1.首先写主函数,创建数组,定义要查找的数字并使其可输入。计算数组长度,便于使用下标查找。
2.使用if语句,如果返回值为-1,则没找到(为什么用-1:因为下标不可能为负数;为什么不用0:因为如果要查找的数字是第一个,下标为0,会混乱),如果返回值为下标,则找到。
3.写调用函数部分:创建左下标left、右下标right、中间下标mid。采用二分查找,使用while循环,如果找到返回值为mid,如果不是则继续循环,如果找不到则返回-1。
int 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[10] = { 1,2,3,4,5,6,7,8,9,10};
int k = 0;
scanf("%d\n", k);
int sz = sizeof(arr) / sizeof(arr[0]);
int ret = search(arr, k,sz );
if (ret == -1)
{
printf("没找到\n");
}
else
{
printf("找到了,下标是:%d\n",ret);
}
return 0;
}
4.3.4 写一个函数,每调用一次这个函数,就会将num的值+1
1.首先写主函数,定义num=0;使其每一次被调用后打印
2.写调用函数,因为在上面我们知道如果我们直接用num是不能改变原来的数值的,所以我们用指针,又因为我们只是改变值不需要返回值,所以用void类型
void Add(int*p)
{
*p = *p + 1;
//或者(*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;
}