详讲函数知识

news/2024/10/30 16:17:57/

目录

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


http://www.ppmy.cn/news/28249.html

相关文章

【c++】STL1—STL初识

文章目录STL的基本概念STL六大组件STL中容器、算法、迭代器容器算法迭代器容器算法迭代器初识vector存放内置数据类型vector存放自定义数据类型容器嵌套容器c的面向对象和泛型编程思想&#xff0c;目的就是复用性的提升。 为了建立数据结构和算法的一套标准&#xff0c;诞生了S…

【计算机网络】网络层IP协议

文章目录一、认识IP协议二、IP协议头部格式三、IP地址划分1. IP地址分类2. 子网划分四、IP地址数量危机1. IP地址的数量限制2. NAT技术五、私网IP和公网IP六、路由1. 认识路由2. 路由表生成算法一、认识IP协议 IP协议是Internet Protocol&#xff08;互联网协议&#xff09;的…

TCP 的演化史-TCP 是一个过渡

TCP 诞生于 1970 年代早期&#xff0c;彼时没有分组交换网的大规模应用&#xff0c;彼时绝大多数通信都在使用电话&#xff0c;电报&#xff0c;电挂等电路交换技术。 诞生在这种环境下的技术不可能脱离时代的影响&#xff0c;如果一个孩子出生在一个父母关系冷漠的家庭&#x…

python的读写操作

一、使用open函数&#xff0c;可以打开一个已经存在的文件&#xff0c;或着创建一个新文件 语法如下&#xff1a; open(name, mode, encoding) name: 要打开的目标文件的字符串(可以包含文件所在的具体路径) mode: 打开文件模式&#xff1a;只读(r)、写入(w)、追加(a)等 e…

2023年全国最新安全员精选真题及答案7

百分百题库提供安全员考试试题、建筑安全员考试预测题、建筑安全员ABC考试真题、安全员证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 11.&#xff08;单选题&#xff09;进入盾构机土仓进行维修工作时&#xff0c;需经&am…

UML类图中的类图、接口图、关联、聚合、依赖、组合概念的解释

文章目录UML类图依赖和关联的主要区别UML类图 类&#xff1a;类有三层结构 第一层&#xff1a;类的名字第二层&#xff1a;类的属性第三层&#xff1a;类的方法 接口&#xff1a;接口跟类相似&#xff0c;不过多了一个<<interface>>来表示它是一个接口 第一层&a…

消失的数字(每日一题)

目录 一、题目描述 二、题目分析 2.1 方法一 2.1.1 思路 2.1.2 代码 2.2 方法二 2.2.1 思路 2.2.2 代码 2.3 方法三 2.3.1 思路 2.3.2 代码 三、完整代码 一、题目描述 oj链接&#xff1a;https://leetcode.cn/problems/missing-number-lcci 数组nums包含从0到n的…

操作系统权限提升(十六)之绕过UAC提权-CVE-2019-1388 UAC提权

系列文章 操作系统权限提升(十二)之绕过UAC提权-Windows UAC概述 操作系统权限提升(十三)之绕过UAC提权-MSF和CS绕过UAC提权 操作系统权限提升(十四)之绕过UAC提权-基于白名单AutoElevate绕过UAC提权 操作系统权限提升(十五)之绕过UAC提权-基于白名单DLL劫持绕过UAC提权 注&a…