目录
1、函数是什么
2、C语言中函数的分类
2.1、库函数:
为什么会有库函数?
怎么学习C语言中的库函数?
2.2、自定义函数
函数的组成:
3、函数的参数
3.1 实际参数(实参):
3.2 形式参数(形参):
4. 函数的调用:
4.1 传值调用
4.2 传址调用
4.3 练习
解答:
5. 函数的嵌套调用和链式访问
5.1 嵌套调用
5.2 链式访问
练习:
维基百科中对函数的定义:子程序
在计算机科学中:
子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit)是一个大型程序中的某部分代码, 由一个或多个语句块组 成。
它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏(就像我们才学C语言常用的scnaf,printf函数一样,我们用这个函数,但是不知道他是怎么实现的)。
这些代码通常被集成为软件库。
大致可以分成两种:(1)库函数,(2)自定义函数
在C语言中我们常常用到一些功能(比如:打印函数,输出函数,等等),每一次我们都要写这样的代码,非常的繁琐和麻烦。
像这种用的次数比较多的,为了提高程序的效率,让程序变的简洁,在C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
我们利用下面这个网站来学习C语言中的库函数:
www.cplusplus.com
界面:界面中有头文件,在头文件中罗列着库函数
简单总结:
C语言常用的库函数都有:
IO函数(标准输入输出) —— input/output printf scanf getchar(输入函数) putchar ......
字符串操作函数 —— strlen(求字符串长度) strcpy(复制字符串) strcmp(比较字符串是否相等) strcat ......
字符操作函数 —— talower(小写变大写) toupper(大写变小写) ......
内存操作函数 —— mencpy menset menmove mencmp ......
时间/日期函数 —— time ......
数学函数 —— sqrt(取根号) abs fabs pow ......
其他库函数
参照文档,学习以下两个库函数:
可以从搜索框中搜索想要找到的库函数
strcpy:
char * strcpy ( char * destination, const char * source );
strcpy介绍界面
我来简单的解读一下这个内容:
函数名字:strcpy——传参类型:字符地址—— 返回类型字符地址。
函数功能:复制字符串 source 到 destination 中,并且也会复制 /0 。
返回值:destination
代码举例:
#include
#include //使用strcpy这个函数需要引用头文件string.h
int main()
{
char arr1[10];
char arr2[10] = "hello";
char* ret = strcpy(arr1, arr2);
printf("%s\n", ret); //打印值:hello
printf("%s\n", arr1); //打印值:hello
return 0;
}
下面这个函数,也可以按照我的这个方式试着使用一下:(如果看不懂英语——可以用翻译软件来翻译)这里我就不过多的介绍了
memset
void * memset ( void * ptr, int value, size_t num );
这个函数的功能是把 ptr 前 num 个字节换成 value 。
补充:使用库函数,必须包含 #include 对应的头文件。
不需要记住全部的库函数
需要学会查询工具的使用
下面是三个比较好的查询工具:
MSDN(Microsoft Developer Network)
www.cplusplus.com
http://en.cppreference.com(英文版)
因为我们的需求是千变万化的,库函数不能满足我们所有的需求,所以程序员就要自己创建函数,来满足不同的需求。
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
但是不一样的是这些都是我们自己来设计。
ret_type fun_name(para1)
{
statement;//语句项
}
// ret_type 返回类型
// fun_name 函数名
// para1 函数参数,可以多个函数
下面我们用两个例子来讲函数:
(1)写一个函数来比较两个数的大小
#include
//函数的接收:其中包括函数名、返回类型、传参和传参类型
int my_max(int x, int y)
{
return x > y ? x : y;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int c = my_max(a, b);
//自己定义一个函数——函数名:my_max——返回类型:整型——传参:整型a和b
//函数的作用是求两个数那个数最大
printf("最大值为:%d\n", c);
return 0;
}
(2)交换两个数的值
//错误示范
#include
void swap(int x, int y)
{
int z = x;
x = y;
y = z;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:%d %d\n", a, b); //打印值为:10 20
swap(a, b);
printf("交换后:%d %d\n", a, b); //打印值为:10 20
return 0;
}
//分析:
//我们传过去两个值:把a和b的值拷贝到x和y中,x和y本身不是a和b,x和y交换了,并不代表a和b交换了。
//实参:a和b传给了—— 形参:x和y —— 传给形参x,y的时候,形参将是实参的临时拷贝
//改变x和y的值并不会改变a和b的值
//正确示范 —— 传a和b的地址
#include
//int* p —— 其中int*表示接收一个地址,px表示指针变量,用来存放地址的。同理int* py也一样。
//因为传过来的是地址,所以要用指针变量来接收。
void swap(int* px, int* py)
{
int z = *px; //其中*为解引用操作符,可以通过地址来找到对应的值
*px = *py; //其中px中存放的是a的地址,所以*px可以找到a。同理通过*py可以找到y的值
*py = z;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:%d %d\n", a, b); //打印值为:10 20
swap(&a, &b); //&为取地址操作符 —— &a为传a的地址 —— a的地址为一段编号。
printf("交换后:%d %d\n", a, b); //打印值为:20 10
return 0;
}
补充:不改变实参的值可以不用传地址,改变实参的值要传地址。
真实传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形 参。
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配 内存单 元),所以叫形式参数。
形式参数当函数调用完成之后就自动销毁了。
因此形式参数只在函数中有效。
下面再来分析一下: ——(2)交换两个数的值
这里可以看到Swap1函数在调用的时候x,y拥有自己的空间,同时拥有了和实参一模一样的内容。
所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量
(1)写一个函数可以判断一个数是不是素数。
(2)写一个函数判断一年是不是闰年。
(3)写一个函数,实现一个整形有序数组的二分查找。
(4)写一个函数,每调用一次这个函数,就会将num的值增加1。
(1)写一个函数可以判断一个数是不是素数。
素数:约数只有1和它本身
#include
#include //用到了库函数sqrt,所以要引用头文件
//函数判断是否为素数
int is_prime(int i)
{
int j = 0;
for (j = 2; j < sqrt(i); j++) //其中sqrt是库函数——开根号
{
if (i % j == 0)
{
return 0;
}
}
return 1;
}
//主函数main
int main()
{
int i = 0;
//判断100到200之间的素数
for (i = 100; i < 200; i++)
{
//规定如果是素数:返回值为1、不是素数返回值为0。
int ret = is_prime(i);
if (ret == 1)
{
printf("%d ", i);
}
}
return 0;
}
(2)写一个函数判断一年是不是闰年。
闰年:能被4整除的并且不能被100整除的年份 和 能被400整除的年份
#include
//函数:判断不是闰年。
int is_leap_year(int i)
{
if (((i % 4 == 0) && (i % 100 != 0)) || (i % 400 == 0))
return 1;
return 0;
}
int main()
{
int i = 0;
//判断1000到2000之间的年份之间的闰年
for (i = 1000; i <= 2000; i++)
{
//规定如果是闰年:返回1、否则返回0。
int ret = is_leap_year(i);
if (ret == 1)
{
printf("%d ", i);
}
}
return 0;
}
(3)写一个函数,实现一个整形有序数组的二分查找。
二分查找原理:(必须是一个有序的数组)
比如:数组:1 2 3 4 5 6 7 8 9 10
第一步:判断0下标和9下标的平均值是否大于要找的数字——平均值为4——下标为4的值为5
第二步:平均值的下标如果大于要找的数字,就判断中间值和9数字的中间值是否大于要找的数字,平均值的下标如果小于要找的数字,就判断0下标和中间值的中间值是否大于要找的数字
第三步:循环做这件事情直到找到数字,或者右边的下标小于左边的下标。
//写一个函数,实现一个整形有序数组的二分查找。
#include
//二分查找数字
int binary_search(int arr[10], int sz, int n)
{
int left = 0;
int right = sz - 1;
int mid = 0;
while (left <= right)
{
mid = (left + right) / 2;
if (arr[mid] < n)
{
left = mid + 1;
}
else if(arr[mid] > n)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组的元素个数
int n = 0;
scanf("%d", &n);
//规定:找到了返回下标,没有找到返回-1
int ret = binary_search(arr, sz, n);
if (ret == -1)
{
printf("没有找到\n");
}
else
{
printf("找到了:下标为:%d\n", ret);
}
return 0;
}
(4)写一个函数,每调用一次这个函数,就会将num的值增加1。
#include
int Print(int num)
{
return ++num;
}
int main()
{
int num = 0;
num = Print(num);
printf("%d\n", num); //1
num = Print(num);
printf("%d\n", num); //2
num = Print(num);
printf("%d\n", num); //3
}
直接用例子说明:
#include
void two()
{
printf("hello\n");
}
void one()
{
two(); //嵌套调用two函数
printf("haha\n");
}
int main()
{
one(); //嵌套调用one函数
printf("hehe\n");
return 0;
}
函数可以嵌套调用,但是不能嵌套定义
什么是嵌套定义:(错误的写法)
int main()
{
void two()
{
printf("hello\n");
}
void one()
{
two();
printf("haha\n");
}
printf("hehe\n");
return 0;
}
把一个函数的返回值作为另外一个函数的参数。
直接举例:
#include
int main()
{
int arr[10] = { 0 };
printf("%d\n", sizeof(arr)); //直接打印sizeof(arr)就是链式访问
return 0;
}
#include
#include
int main()
{
char arr1[10] = "xxxxxxx";
char arr2[10] = "hehe";
printf("%s\n", strcpy(arr1, arr2)); //直接打印strcpy(arr1, arr2)的返回值就是链式访问
return 0;
}
下面打印值为多少:
#include
int main()
{
printf("%d", printf("%d", printf("43"))); //打印值为4321
return 0;
}
//分析:printf库函数有返回值,返回值为打印值得个数。
//printf("43") —— 在屏幕上打印43 —— 返回值为2 —— printf("%d", printf("%d", 2 ));
//printf("%d", 2 ) —— 在屏幕上打印2 —— 返回值为1 —— printf("%d", 1)
//printf("%d", 1) —— 在屏幕上打印1 —— 返回值为1 —— 4321
补充:scanf库函数也是有返回值的:返回值为:读取的有效数字个数。