本章亮点
- 传值调用的核心:形参实例化之后相当于实参的一份临时拷贝
- 传址调用:实现在函数内部直接操作函数外部的变量
- 想要成功交换两个变量的值,需要借助第三个临时变量
- 带你学会借助学习网站使用库函数
【C语言详解】现学现用库函数,调用,声明和自定义函数
1.计算机程序和子程序
程序来源于生活,在说官话之前咱先一起吃个栗子!
如下图,图中的六个步骤就构成了一个生活中的程序,该程序的功能是让人到银行取到钱。图中的六个动作执行过程可视为六个子程序。
Q:类比生活中的程序和子程序,所以什么是计算机程序和子程序呢?
生活中的程序 | 计算机程序 |
---|---|
人类语言描述的 | 编程语言编写的 |
一系列方式/动作执行过程 | 一系列有序语句的集合 |
该系列动作/方式能使人完成某件事情 | 该集合能让计算机执行某些操作或解决某个问题 |
生活中的子程序 | 计算机子程序 |
执行该套方式/动作下的具体步骤 | 该集合下的某些语句/某些语句块 |
A:计算机程序就是利用编程语言编写的一系列有序指令的集合,该集合能让计算机执行某些操作或解决某个问题 。 计算机子程序就是计算机程序中的某些语句/某些语句块。函数是一种子程序。
2.C语言中的函数
- 在C语言中,函数就是一种子程序,结合生活中的子程序理解,子程序/函数就是计算机程序中的某些语句或某些语句块。子程序 的官话如下:
在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
- 在数学中,函数有三要素,分别是自变量x,因变量y,对应关系。已知x,根据对应关系,就能求出y。其实C语言中的函数同数学中函数是一样的。在C语言函数中,与数学中三要素一一对应的的分别是参数,返回值,函数名(功能)。已知参数,根据函数名代表函数能实现的功能或操作,输出返回值。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
看来咱和官方对函数的理解不谋而合呢,那接下来就让我们进入到今天的第一个重点:库函数。
3.库函数
C语言中的函数分为两大类,即库函数和自定义函数。
3.1库函数的发展
- 在C语言发展早期,并没有库函数。
- 随着C语言的不断发展,人们在写代码时发现:有一些基础功能或操作总被频繁的使用。比如:把数据信息按照一定格式打印到屏幕上(printf);拷贝字符串strcpy;计算k的n次方(pow)等等。
- 因为这些基础功能或操作被程序员们广泛使用,同时它们是非业务性代码,所以C语言把这些基础功能或操作集成库,并根据C语言的标准规定了库的标准,后来统称库函数。库函数的出现大大提高了程序的效率和代码的可移植性。
3.2库函数是什么?
库函数是一系列能实现基础功能和操作的函数。
3.3库函数千千万,如何查找/学习库函数?
- 在这里给大家推荐第一个库函数的学习网站是cplusplus。点击该网站的右上角Legacy Version(旧版本),就可以切换到该网站的旧版本中。该网站的旧版本中还保留着搜索库函数的功能。
- 给大家推荐第二个库函数的学习网站是c/c++的官网。与cplusplus网站相比,它的好处是它有中文版本。
3.4现学现用库函数
-
使用库函数时,必须在程序入口前声明该库函数所属的头文件
-
在代码中使用库函数前,去学习网站搞清楚它的头文件,参数和参数类型,返回值和返回值类型,库函数名,库函数功能 以及 语法规则。
-
接下来让我们吃两个栗子实操一下:
-
栗子1:编写一段代码,要求使用库函数strcpy
(1). 来到学习网站cplusplus学习strcpy, cplusplus中对strcpy的解释如图:
通过上图,我们知道:1.strcpy是Copy string即拷贝字符串的意思。
2.库函数strcpy的功能是:Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point)即将source指向的C字符串复制到destination指向的数组中,包括结束的null字符(并在该点停止)。
3.库函数strcpy的参数是destination和source。他们的类型非别是char * 和const char * 。
4.库函数strcpy的返回值是destination,它的类型是char * 。
5.库函数strcpy的语法规则是:
strcpy( destination, source);
6.库函数strcpy的头文件是string.h(2). 写代码:把数组arr1中的字符串复制到数组arr2和arr3中
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>//使用库函数前引头文件
int main()
{
char arr1[] = "abcdef";//source参数是数组arr,参数类型const char*。
char arr2[20] = {0};//destinatonc参数是数组arr2和数组arr3,参数类型是char*
char arr3[20] = {0};
//把source参数中的字符串复制到参数destination中
strcpy(arr2,arr1);
strcpy(arr3, "abcdef");
printf("%s\n", arr2);
printf("%s\n", arr3);
return 0;
}
运行结果:
代码分析:很基础但还是在这里强调一下:
- 为什么学习网站中说参数类型是char*,而我在代码中写的是char?因为数组名就是地址。
- 为了避免溢出,destination指向的数组的大小应该足够长,以包含与source相同的C字符串(包括结束的null字符),并且不应该在内存中与source重叠。不要犯数组越界的错误哦!
- 我们知道字符串"abcdef"的真实面目是’a’,‘b’,‘c’,‘d’,‘e’,‘f’,‘\0’,其中’\0’是字符串的结束标志。即复制的内容中包括’\0’,且复制到’\0’停止复制。
-
栗子2:编写一段代码,要求使用库函数memset
(1):来到学习网站cplusplus学习们memset, cplusplus中对memset的解释如图:
通过上图,我们知道:
1.库函数memset的头文件时string.h
2.memset是Fill block of memory(填充内存块) 的意思。在英文中,memory是记忆的意思,但在计算机中,memory是内存的意思。
3.库函数memset的功能是:Sets the first num bytes of the block of memory pointed by ptr to the specified value (interpreted as an unsigned char).即将ptr指向的内存块的前num个字节设置为指定的值(解释为unsigned char)。
4.库函数memset的参数是ptr,value,num,他们的参数类型分别是void*,int,size_t。
5.库函数memset的返回值是ptr,返回类型是void*。
6.库函数memset的语法规则是:
memset( ptr, value,num);
(2).写代码:把数组arr的前4个字节,内容设置为d
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "dianzan shoucang guanzhu";
memset(arr,'d',4);
printf("%s\n", arr);
return 0;
}
运行结果:
吃栗子时间结束!
C语言中常用的库函数包括:IO1 函数,字符串操作函数(如strcpy),字符操作函数,内存操作函数(memst),时间/日期操作函数,数学函数以及其他库函数。虽然库函数千千万,但库函数并不需要我们特别记忆。在知道了上面两个网站后,当我们在代码中需要用到库函数时,只需要仿照上面吃栗子时,查阅网站,找到所需的库函数现学现用即可。
不过现学现用是很爽,但是库函数只是一系列能实现基础功能和操作的函数,所以如果我们想要写代码实现我们自己心中所想,必须掌握自定义函数!
4.自定义函数
- 自定义函数:根据自定义函数在main函数中的调用需求,程序员自己设计函数的参数(参数类型),返回值(返回值类型),函数名,函数功能。
- 即库函数的资料我们可以从学习网站上获得。而自定义函数的资料需要程序员自己设计。
4.1自定义函数的基本形态:
ret_type fun_name(par_type paral)
//返回值类型 函数名 参数类型 (形)参数
{
statement;//语句项
}//{}里是函数体,在{}里写代码实现函数功能
注意:返回值类型如果定义为void,表示这个自定义函数不返回值。
4.2自定义函数的实现步骤(先调后定):
-
- 在main函数中根据具体需求调用自定义函数,类似于库函数的使用
- 在main函数之外,根据调用需求定义函数的参数(参数类型),返回值(返回值类型),函数名,函数功能。
-
代码演示:写一个自定义函数,函数的功能是找出两个整数中的最大值。(先调后定)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//在调用GetMax时,我们得知GetMax的功能时比较int类型参数a和int类型参数b的大小,最后输出较大的参数。
//所以根据函数调用,我们设计出GetMax函数如下:
int GetMax(int x, int y)//传参
{
//函数功能:找出两个整数中的最大值
int z = 0;
if (x > y)
z = x;
else
z = y;
return z;//输出返回值到main函数
}
int main()
{
int a = 10;
int b = 20;
//函数的调用(类似于库函数的使用)
int max = GetMax(a, b);//函数名要起的有意义
//接收返回值 //传(实)参
printf("%d\n", max);
return 0;
}
调试结果:
图片演示:
4.3函数的参数
- 实参
- 在main函数中调用自定义函数时,此时真实传给函数的参数,叫实际参数,也称实参。上面GetMax函数中的参数a,b都是实参。
- 实参可以是:常量,变量,表达式,函数等。但无论实参是何种类型的量,在进行函数调用时,实参必须是明确的值,以便把这些值传递给形参。
- 形参
- 在main函数之外,我们自己设计自定义函数时,函数名后括号中的参数叫形式参数,也称形参。上面GetMax函数中的参数x,y都是形参。
- 在函数未被调用时,形参只是个摆设。
- 形参只有在函数被调用的过程中才实例化(分配内存单元),并且当函数调用完成后形参的内存单元自动销毁。所以形参只在自定义函数中有效(即形参的生命周期同局部变量一样)。上面GetMax函数被调用完成后,空间x,空间y,被自动销毁。
4.4函数的调用
-
如何理解想要成功交换两个变量的值,需要借助第三个临时变量?
(1). 先吃个栗子
借助第三个空瓶,第一步把酱油倒进空瓶中,酱油瓶空了!第二步把醋倒进酱油瓶中,,醋瓶空了!第三步把酱油倒进醋瓶中,醋和酱油成功进入了对方的瓶子中!
(2). 两个变量值(酱油和醋)分别被存放在计算机内存中的两块空间(酱油瓶和空瓶)里,所以如果我们想要成功交换两个变量的值,必须向内存申请一块空白空间(空瓶),即借助第三个临时变量。
4.41. 传值调用
传值调用是指:在main函数中调用自定义函数时,在传参过程中,实参传给形参的仅仅是实参的值。形参实例化后相当于只是实参的一份临时拷贝,在自定义函数内部,对形参的修改不会影响实参。
代码演示:设计一个自定义函数,函数的功能是交换两个整型变量的内容
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Swap(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);
Swap(a, b);
printf("交换后:a = %d,b = %d", a, b);
return 0;
}
运行结果:
函数设计失败原因:
自定义函数Swap在被调用的时候,传参过程中,实参a,b仅仅把各自的值10,20传给了形参x和y。也可以说形参x和y临时创立空间,仅仅拷贝了参数a和b的值(传值调用)。所以对形参的修改不会影响实参。
4.42.传址调用
传址调用是指:在main函数中调用自定义函数时,在传参过程中,实参传给形参的是实参的地址。此时使函数的实参和形参建立了真正的联系,即可以在自定义函数内部通过操作形参直接改变实参。
代码演示:设计一个自定义函数,函数的功能是交换两个整型变量的内容
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//函数功能:交换两个整型变量的内容
void Swap(int* pa, int* pb)
{
int z = 0;
z = *pa;
*pa = *pb;
*pb = z;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:a = %d,b = %d\n", a, b);
Swap(&a, &b);
printf("交换后:a = %d,b = %d", a, b);
return 0;
}
运行结果:
代码分析:函数Swap在被调用的时候,传参过程中,实参传给形参的是&a,&b,即pa空间和pb空间里存放的分别是空间a和空间b的地址。&a和&b相当于两个链接,它们可以直接跳转到变量a和b所处的空间。而*pa,*pb则分别代表的是链接背后,空间a和空间b中的变量。
所以代码中,z=*pa执行的操作是:把空间a中的值10赋给空间z。*pa=*pb执行的操作是:把空间b中的值20赋给空间a,*pb=z执行的操作是:把空间z的值赋给空间b。
4.43.嵌套调用
I和O分别代表input,output。 ↩︎