C_C语言function的使用

来源:互联网 发布:淘宝客服大概多少工资 编辑:程序博客网 时间:2024/05/16 01:39

下面我们来介绍C语言的函数的定义与应用:
首先我们讨论一下我们为什么要使用函数,其实我们知道一个C语言本身就有一个函数main函数,他可以用来执行我们所编写的代码,那么既然如此那我们为什么还要
自己再去编写我们自己的函数呢?首先,当我们要写的程序比较小规模时我们实际上是无需编写函数的。不过,如果一旦我们编写代码量比较大的程序的话,就会发
现随着我们代码量的增加,代码的逻辑就会越来越复杂,等到达了一定的规模之后这种复杂程度就会变得不可接受,所以我们需要将一个大规模的代码通过函数拆分
为很多小规模的代码块,以减低程序的复杂程度。其次,我们知道我们在编写程序是经常会用到比如printf、scanf、strcpy、strcmp......等等一些函数,但是我们
发现我们并没有定义这些函数,其实这些函数都是C语言提供的标准函数库内的库函数,其实我们也可以自己写一个函数库来供自己或他人使用。最后就是如果一个程
序规模较大,时间要求又很紧张,那么我们就需要很多人来一起完成这个程序,而函数的应用则可以很好的去支持这种开发方式。综上所述我们使用函数编程时很有必要的......
好了,既然我们已经认识到了函数编程的重要性了,那么下面我们就一起来学习一下如何进行函数式的编程。
最先我们还是看一段代码示例:

首先我来看函数大致的定义与使用,我们首先来看第14行他是声明了一个函数(我们在这里重点强调在C语言里所有的函数都是先声明后使用,绝对不允许使用一个还
未声明的函数,哪怕就在其后声明的函数也不允许使用),我们可以看到这个函数的第一个标示是一个int这代表了这个函数的返回值是一个int后面的sort是这个函数
的名称,而后面的括号就是函数的参数列表最后一对大括号表明函数声明的结束(我们看到在第15行有一个和14行完全一样的函数声明,写在这里事项告诉各位一个函
数可以声明多次,但是只能定义一次)。我们再来看第20行,这就是我们使用函数(函数的调用),当我们调用函数那么程序就不会再继续向下执行,而是跳到函数的
定义处继续执行(也就是第29行),我们看到在函数的定义中写了一个函数的排序算法。最后函数执行完毕,到达了return语句,return语句会返回一个值作为我们函
数的返回值,并且回到我们函数调用的位置(第20行)然后继续向下执行......
接下来我们分析一下函数的返回值:
一个返回类型大致分为两个类型:有返回值和无返回值,同时就C语言以及大多数语言(C/C++/Object-C/C#/java/Python.....)来讲都仅支持返回一个值(但是少数语
言支持多个返回值例如Lua等)。如果函数无返回值就在函数最前使用void关键字,如果函数有返回值就写明返回值的类型(基本数据类型、数组、枚举、联合、结构体
等等),那么既然函数有返回值那么就需要在函数体的末尾使用return来返回相应类型的值,我们看代码示例:

下面我们再来看函数的参数列表,我们首先还是说明一个函数分为声明和定义两个部分,其实函数在声明和定义时他们的参数列表大致使一样的,只是有一点点的区别
我们在这里说明:我们知道函数的参数列表就是定义了一系列的变量,而其实一个变量最少分为两个部分,变量的类型以及变量名。我们为什么需要参数列表?就是因
为我们需要在函数体内部使用这些变量,但是我们想函数的声明是没有函数体的,所有他自然是不需要变量名的,但是我们必须给他声明变量的类型。参数列表大致分
为不允许传参、传任意参数、传声明的参数,不限定传参四种类型:
当我们的参数列表为空时他并不代表没有参数,而是代码任意参数,也就是说,当调用这个函数时,你可以为这个函数的参数列表传递任意类型任意个数的参数,如果
不想让人调用时传递参数应该怎么办?你只需要在参数列表内添加一个void关键字即可(记住如果使用了void就不能再在参数列表内声明任何参数,也就是说参数和
void关键字在参数列表内是不能共存的)。当然你也可以直接声明一个参数列表。最后你还可以声明一个变长参数列表。
说了这么多,下面我们看代码:


我们通过代码示例看到了各种类型的参数列表,不过最让人好奇是变长参数列表的变长参数究竟是如何使用的,各位不知道有没有想到我们经常使用的两个行数?他们
就是printf、scanf这两个函数实际上是第一个参数时const char*类型的参数,后面就是一个变长参数。那我们能不能自己定义一个printf函数呢?当然可以。
看下面就是我们自定义的与printf具有相似功能的函数:


从上面的示例代码中我们看到我们所写的代码的运行结果完全正确那么接下来我就想给大家解释一下我们的代码究竟是如何运行的:
如果我们想要处理变长参数首先我们就要引入头文件#include<stdarg.h>这个头文件声明了我们处理变长参数所需要的一个类型和三个函数他们分别是va_list类型以及
函数va_start、va_arg、va_end首先我们需要通过va_start来获取可变长参数的所有参数,我们要获得参数就必须告诉函数我们从哪一个参数开始往后的所有参数都是
变长参数的参数(显而易见在我们的函数中参数ch之后的所有参数都是变长参数的一部分),获得所有的变长参数后需要将他们保存在一个变量中,这个变量的类型就是
va_list,下面我们就可以循环获得参数了,我们通过va_arg函数来循环获取参数的值,这个时候我们需要告诉va_arg函数我们要从那个v_list变量中获得参数,要获得
哪一种类型的值,最后va_arg函数的返回值就是我们要获得的参数的值(系统会自动为我们记录当前位置,每次取值后都会后移一个,当我们再次调用va_arg函数时,他
就会自动返回),当我们获取到所有的参数后调用va_end函数来关闭。
下面我们看看上述所使用的工具集的声明:

好下面我们正式解释我们所写的函数,我们看到main函数式非常简单的就是我们调用我们自己的打印函数,大打印个人信息:名称、年龄、收入、性别他们分别对应char*
、int、double、char而函数的返回值是我们一共有几个参数。
下面我们到我们自定义的打印函数中去一探究竟:
首先我们定义了数个变量:
第31行:int len;     //用于保存第一个参数字符串变量的长度
第32行:int num;     //我们每当搜索到一个参数时加一,保存的是参数的总个数,最后返回
第33行:int i;       //循环控制变量
第35行:int t1;      //如果我们搜索到int使用此变量接受参数的值
第34行:char s[3];   //当需要打印转义字符时临时保存变量
第36行:char t2;     //如果我们搜索到char使用此变量接受参数的值
第37行:char *t3;    //如果我们搜索到char*使用此变量接受参数的值
第38行:double t4;   //如果我们搜索到double使用此变量接受参数的值
第40行:   //我们调用va_start函数将ch参数以后的所有参数都认为是变长参数所包含的参数
第41行:   //我们开始循环遍历字符串以搜索我们有几个变长参数以及变长参数的类型
第42行:   //一旦我们搜索到'%'就说明我们已经搜索到了一个参数然后i++以后移一位然后num++记录参数加一
第45行:   //我们进入switch结构根据'%'后面的字符进入相应分支以获取对应的类型
第46行:   //当我们搜索到整型变量
第47行:   //我们通过va_arg函数在list中获取一个int类型的参数,返回
第48行:   //打印搜索到的参数
第49行:   //结束本次switch
第50行:   //其余分支与第一个分支类似.................................................
第66行:   //当搜索到一个转义字符................
第67行:   //构造转义字符的字符串................
第69行:   //打印转义字符串
第71行:   //当搜索到的字符是一个普通字符直接打印
第75行:   //结束va_list
第76行:   //函数结束,返回搜索到的参数的个数

在最后我们来讨论一下函数的传参问题:
我们将一个变量传入函数后,那么这个函数真的被传入到了函数当中去了吗?我们先看我们的代码示例再做结论:

(首先申明这用到了一些关于内存的基本概念:我们的每个变量都是保存在内存中的栈中的,而每个变量都有一个地址,如何判断我们的两个变量是不是同一个?
我们只需比较他们保存的地址就是可以了,如果两个变量保存在同一个地址那么他们就是同一个变量否则就不是同一个变量。PS:其实C语言是没有字符串类型的
,所谓的字符串就是保存在一个连续内存空间的字符组合,而字符串变量实际上市一个指针(保存内存地址的变量),这个变量保存了这段内存的起始地址,而这
段地址以'\0'结束)
我们看到在main函数和fun函数中int i的内容是一样的,但是地址不一样,char *ch的内容一样指向的是同一块地址,当然那个字符串也是同一个字符串了。但是
两个变量的地址不同所以他们不是同一个变量。
由以上结果我们得出当函数进行传参时会创建同样类型的变量并将实参的内容拷贝到创建的副本当中来,所以我们在函数内部操作的是实参的副本,并不会影响到
实参的值;

最后我们将介绍一些我们最常见的库函数:


sprintf:字符串格式化命令,主要功能是把格式化的数据写入某个字符串中:int sprintf( char *buffer, const char *format, [ argument] … );
printf:printf()函数是格式化输出函数, 一般用于向标准输出设备按规定格式输出信息:printf("<格式化字符串>", <参量表>);
scanf:格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中:intscanf(constchar*format,...);
malloc:malloc的全称是memory allocation,中文叫动态内存分配,当无法知道内存具体位置的时候,想要绑定真正的内存空间,
就需要用到动态的分配内存。 void *malloc(unsigned int num_bytes)。
free:释放ptr指向的存储空间:void free(void *ptr)
fopen:打开一个文件:FILE * fopen(const char * path,const char * mode);
fread:从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读取到的元素个数,如果不成功或读到文件末尾返回 0。
fwrite:向文件写入一个数据块:size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
fprintf:fprintf()函数根据指定的format(格式)发送信息(参数)到由stream(流)指定的文件:int fprintf( FILE *stream, const char *format [, argument ]...);
fscanf:从一个流中执行格式化输入,fscanf遇到空格和换行时结束,注意空格时也结束:int fscanf(FILE*stream,constchar*format,[argument...]);
fclose:关闭一个流:flose()
strcpy:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间:extern char *strcpy(char* dest, const char *src);
strcmp:比较两个字符串。设这两个字符串为str1,str2,若str1==str2,则返回零;若str1>str2,则返回正数;若str1<str2,则返回负数:extern int strcmp(const char *s1,const char * s2);

OK,关于C语言的函数我们就讲到这。

0 0
原创粉丝点击