序言.
本文主要通过《明解C语言》和《C语言程序现代设计》两本书,对C语言函数知识进行一个简要的涵盖与总结.
一.函数是什么
程序是由一个个小的零件组成的,而函数就是这种“零件”的一个较小的单位.
在数学上,我们经常可能会听到函数的概念,但不同于数学上的函数,C语言中的函数不一定需要参数,也不一定需要计算数值,两者之间是有差别存在的.
上图是来自维基百科的定义,在其中一大段话中,我们可以大致了解到函数的两大明显作用.
1.使我们的代码具备相对独立性
把需要重复的代码块单独封装成一个函数,这样可以使编程不会出现语句冗余的现象,大大增强了程序的可读性和编程的趣味性.
2.提供对过程的封装和细节的隐藏
在调用函数实现功能时,我们往往只需要提供参数,函数名即可,具体如何实现,真的可能未必知道,比如我们常用的printf函数,我们就不一定知道具体它是如何实现的,但我们可以调用它.
下图,也可以用类似电路图图形的方式展现给我们看求最大值的一个函数.
二.函数的分类
1. main函数和库函数
在C语言中,main函数作为程序的入口,是必不可少的.一旦程序运行,就会执行main函数的主体部分.
main函数中使用的printf,scanf,puts等等一系列函数,就是我们常称的库函数.C语言的基础库中提供了一系列类似的库函数,它们不是业务性的代码,但我们在开发的过程中每个程序员都可能用的到,通过提供这样的库函数,就能够大大提高代码的可读性和可移植性.
想了解哪个函数,我们简单一搜,就能全部知道了.
这里来模拟一下strcpy这个库函数的实现,打开网站搜索,有如下结果
它的参数有两个,一个是指向destination的char *指针,一个是指向source的char *指针,发挥的作用是,将source指向的字符串内容拷贝到destination指向的字符串中,包括/0也一起拷过去.
1.src指向的内容我们不需要它修改,按照cplusplus上的定义,加上const修饰,即便后面弄反dest,src,编译器也不会通过.
2.返回的是一个char*的指针,而不是void设计
3.assert函数报错,避免两者指针中有一个为空指针
4.将循环体并入循环条件中,直到src解引用后为/0,赋给*dest后,循环停止.
2. 自定义函数
同时一定记得函数名后加圆括号,否则就会报错.
三.函数的参数
1.实参
2.形参
四.函数的调用
为了了解函数,函数的定义和函数的调用都是必不可少的,前者时站在设计者的角度,后者则是站在使用者的角度.
1.传值调用
2.传址调用
//实现两个数的交换
void Swap1(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
void Swap2(int *px, int *py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int num1 = 1;
int num2 = 2;
Swap1(num1, num2);//传值调用,无法进行交换两个数的操作
printf("Swap1::num1 = %d num2 = %d\n", num1, num2);
Swap2(&num1, &num2);//传址调用,通过解引用指针,访问变量num1,num2的空间
printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
return 0;
}
五.函数的嵌套调用和链式访问
1.嵌套调用
顾名思义,在函数中调用另一个函数,main函数中使用printf函数实际上也是函数的嵌套调用.
2.链式访问
把一个函数的返回值作为另外一个函数的参数
这里strcat返回的指针,提供给strlen函数,然后strlen函数计算字符串的长度,这里我们还模拟实现strcpy函数,当然只是其中一种模拟实现my_strcpy函数的方式.
六.函数的声明和定义
1.函数声明
像上述average函数,如果放在main函数之后,不会报错,但它会默认返回值是一个int类型,直到运行到后面发现是double类型,于是会报错.
解决这个问题的方法就是函数声明
函数声明能够使编译器先对函数进行概要浏览,而函数定义可以之后再给出.
在main函数前加上这条,那main函数就知道average函数返回的值是double类型.
2.函数的定义
#ifndef __TEST_H__
#define __TEST_H__
//函数的声明
int Add(int x, int y);
#endif //__TEST_H__
#include "test.h"
//函数Add的实现
int Add(int x, int y) {
return x+y; }