您的位置:首页 > 编程语言 > C语言/C++

iOS第五天 C语言 函数

2016-01-11 18:59 323 查看
# 函数基本概念

##1.函数的概述

- C源程序是由**函数组成**的。虽然在前面各章的程序中大都只有一个主函数main(),但实用程序往往由多个函数组成。函数是C源程序的基本模块,通过对函数模块的调用实现特定的功能。

- C语言不仅提供了极为丰富的库函数(如Turbo C,MS C都提供了三百多个库函数),还允许用户建立自己定义的函数。**用户可把自己的算法编写成一个个相对独立的函数,然后再需要的时候调用它。** 可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为**函数式语言**.

- 比如你用C语言编写了一个MP3播放器程序,那么它的程序结构如下图所示:

程序段(函数)

(功能)播放

程序段(函数)

(功能)暂停

程序段(函数)

(功能)停止

+你应该把实现某个功能所需的代码都写在函数中。比如有个函数的功能是播放MP3,那么播放MP3的代码都应该写到这个函数中。

+当调用(执行)一个函数时,计算机就会按顺序执行函数中的所有代码,从而展示函数所实现的功能。

##2.函数的分类

- 在C语言中可从不同的角度对函数分类

- 从函数定义的角度看,函数可分为库函数和用户定义函数两种。 stdio.h stdlib.h

+库函数:由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用到printf、scanf、getchar、putchar、gets、puts、strcat等函数均属此类。

+用户定义函数:由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。

- C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回值函数两种。

+有返回值函数:此类函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。如数学函数即属于此类函数。由用户定义的这种要返回函数值的函数,必须在函数定义和函数说明中明确返回值的类型。

+无返回值函数:此类函数用于完成某项特定的处理任务,执行完成后不向调用者返回函数值。这类函数类似于其它语言的过程。由于函数无须返回值,用户在定义此类函数时可指定它的返回值为“空类型”,空类型的说明符为“void”。

- 从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。

+无参函数:函数定义、函数说明及函数调用中均不带参数。主调函数和被调函数之间不进行参数传送。此类函数通常用来完成一组指定的功能,可以返回或不返回函数值。

+有参函数:也称为带参函数。在函数定义及函数说明时都有参数,称为形式参数(简称为形参)。在函数调用时也必须给出参数,称为实际参数(简称为实参)。进行函数调用时,主调函数将把实参的值传送给形参,供被调函数使用。

- C语言提供了极为丰富的库函数,这些库函数又可从功能角度作以下分类。

+字符类型分类函数:用于对字符按ASCII码分类:字母,数字,控制字符,分隔符,大小写字母等。

+转换函数:用于字符或字符串的转换;在字符量和各类数字量(整型,实型等)之间进行转换;在大、小写之间进行转换。

+目录路径函数:用于文件目录和路径操作。

+诊断函数:用于内部错误检测。

+图形函数:用于屏幕管理和各种图形功能。

+输入输出函数:用于完成输入输出功能。

+接口函数:用于与DOS,BIOS和硬件的接口。

+字符串函数:用于字符串操作和处理。

+内存管理函数:用于内存管理。

+数学函数:用于数学函数计算。

+日期和时间函数:用于日期,时间转换操作。

+进程控制函数:用于进程管理和控制。

+其它函数:用于其它各种功能。

+以上各类函数不仅数量多,而且有的还需要硬件知识才会使用,因此要想全部掌握则需要一个较长的学习过程。应首先掌握一些最基本、最常用的函数,再逐步深入。由于课时关系,我们只介绍了很少一部分库函数,其余部分读者可根据需要查阅C语言函数手册。

+还应该指出的是,在C语言中,所有的函数定义,包括主函数main在内,都是平行的。也就是说,在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。但是函数之间允许相互调用,也允许嵌套调用。习惯上把调用者称为主调函数。函数还可以自己调用自己,称为递归调用。

+main 函数是主函数,它可以调用其它函数,而不允许被其它函数调用。因此,C程序的执行总是从main函数开始,完成对其它函数的调用后再返回到main函数,最后由main函数结束整个程序。一个C源程序必须有,也只能有一个主函数main。

# 函数的定义

##1.自定义函数的书写格式

- 用户定义函数:由用户自己写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且在主程序中需声明,然后才能使用。

返回值类型函数名(参数类型形式参数1,参数类型形式参数2,…)

{

函数体

}

- 示例:

int main(){

}

void test1(){

}

##2.定义函数的目的

- 将一个常用的功能封装起来,方便以后调用

##3.定义函数的步骤

- 函数名:函数叫什么名字

- 函数体:函数是干啥的,里面包含了什么代码

- 返回值类型: 函数执行完毕返回什么和调用者

##4.无参无返回值函数定义

- 格式

void 函数名()

{

函数体

}

- 示例:

void sayHello()

{

printf("hello");

}

- 没有返回值时return可以省略

##5.无参有返回值函数定义

- 格式

返回值类型函数名()

{

函数体

return0;

}

- 示例

int getAge()

{

return 30;

}

##6.有参无返回值函数定义

- 格式

void 函数名(参数类型 形式参数1,参数类型 形式参数2,…)

{

函数体

}

- 示例

void prtLines(int n)

{

for (int i=0; i<n; i++) {

printf("------");

}

}

- 形式参数表列表的格式:类型 变量名,类型 变量2,.......

##7.有参有返回值函数定义

- 格式

返回值类型函数名(参数类型形式参数1,参数类型形式参数2,…)

{

函数体

return0;

}

- 示例:

int getMax(int number1, int number2)

{

return number1 > number2 ? number1 : number2;

}

##8.函数定义注意

- 函数不能嵌套定义

void test(){

void test2() // 错误写法

{

}

}

# 函数的参数

##1.形式参数介绍和使用

- 基本概念

+形式参数:在定义函数时,函数名后面的小括号()中定义的变量称为形式参数,简称形参

+形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。

int max(int number1, int number2) // 形式参数

{

return number1 > number2 ? number1 : number2;

}

##2.实际参数介绍和使用

- 基本概念

+实际参数:在调用函数时传入的值称为实际参数,简称实参

+实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值。

int main()

{

max(88, 99); // 实际参数

return 0;

}

##3.形式参数、实际参数注意点

- 调用函数时传递的实参个数必须和函数的形参个数必须保持一致

int max(int number1, int number2) // 形式参数

{

return number1 > number2 ? number1 : number2;

}

int main()

{

max(88); // 报错

return 0;

}

- 当使用基本数据类型(char、int、float等)作为实参时,实参和形参之间只是值传递,修改形参的值并不影响到实参函数可以没有形参

void change(int number1, int number2)// 形式参数

{

number1 = 250; // 不会影响实参

number2 = 222;

}

int main()

{

int a = 88;

int b = 99;

change(a, b);

printf("a = %d, b =%d", a, b);

return 0;

}

输出结果: 88, 99

- 形参实参类型不一致,
会自动转换为形参类型


void change(double number1, double number2)// 形式参数

{

printf("number1 = %f, number2 = %f", number1, number2);

}

int main()

{

change(10, 20);

return 0;

}

输出结果: 10.000000, 20.000000

##4.返回值类型注意点

- 如果没有写返回值类型,默认是int

int max(int number1, int number2) // 形式参数

{

return number1 > number2 ? number1 : number2;

}

可以写为

max(int number1, int number2) // 形式参数

{

return number1 > number2 ? number1 : number2;

}

##5.return使用注意

- 一个函数内部可以多次使用return语句,使用了return语句后,函数就会马上停止执行,return语句后面的代码就不再被执行

int max(int number1, int number2) // 形式参数

{

return number1 > number2 ? number1 : number2;

printf("执行不到"); // 执行不到

return 250; // 执行不到

}

- 函数返回值的类型和return实际返回的值类型应保持一致。如果两者不一致,则以返回值类型为准,自动进行类型转换

float height()

{

return 175;

}

int main()

{

printf("%d", height());

}

输出结果: 175.000000

# 函数的声明

##1.函数的声明

- 在C语言中,函数的定义顺序是有讲究的:默认情况下,只有后面定义的函数才可以调用前面定义过的函数

- 如果想把函数的定义写在main函数后面,而且main函数能正常调用这些函数,那就必须在main函数的前面进行函数的声明

- 函数的声明格式:

+返回值类型 被调函数名( 类型 形参, 类型 形参... );

+返回值类型 被调函数名( 类型, 类型...);

例如:

int max( int a, int b );

int max( int, int );

- 函数的声明与实现的关系

![](http://7xj0kx.com1.z0.glb.clouddn.com/hssm.png)

- 注意点:

+如果被调函数的返回值是整型时,可以不对被调函数作说明,而直接调用。这时系统将自动对被调函数返回值按整型处理

int main(int argc, const char * argv[])

{

printf("result = %d\n", getMin());

return 0;

}

int getMin() // 返回int, 不用声明

{

return 10;

}

int getMax() // 返回不是int,
需要声明

{

return 10.10;

}

+ 当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数再作说明而直接调用。

float getMax() // 在调用之前定义

{

return 10.10;

}

int main(int argc, const char * argv[])

{

printf("result = %f\n", getMax());

return 0;

}

# 函数的调用

##1.函数的调用

- 函数调用的一般形式为

+函数名(实际参数表);

+对无参函数调用时则无实际参数表。

+实际参数表中的参数可以是常数,变量或其它构造类型数据及表达式。

+多个参数各实参之间用逗号分隔。

- 函数表达式

+函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。

int z=0;

z = max(23, 80) + 1; // 函数表达式

- 函数语句

+函数调用的一般形式加上分号即构成函数语句。

printf("%d",a);

scanf("%d",&b);

- 函数实参

+函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。

printf("%d\n",max(20, 30));

##2.函数的嵌套调用

- C语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题。但是C语言允许在一个函数的定义中出现对另一个函数的调用。这样就出现了函数的嵌套调用。即在被调函数中又调用其它函数。这与其它语言的子程序嵌套的情形是类似的。

# 递归函数

##1.递归函数概述及用法

- 一个函数在它的函数体内调用它自身称为递归调用。这种函数称为递归函数。C语言允许函数的递归调用。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层。

void function(int x)

{

function(x);

}

- 这个函数是一个递归函数。但是运行该函数将无休止地调用其自身,这当然是不正确的。为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。

##2.递归函数构成条件

- 自己调用自己

- 存在一个条件能够让递归结束

- 问题的规模能够缩小

- 案例: 获取用户输入的数字, 直到用户输入一个正数为止

void getNumber()

{

int number = -1;

while (number < 0) {

printf("请输入一个正数\n");

scanf("%d", &number);

}

printf("number = %d\n", number);

}

void getNumber2()

{

int number = -1;

printf("请输入一个正数abc\n");

scanf("%d", &number);

if (number < 0) {

// 负数

getNumber2();

}else{

// 正数

printf("number = %d\n", number);

}

}

-能用循环实现的功能,用递归都可以实现

- 递归就是自己调用自己,性能差

- 注意:递归一定要有一个明确的结束条件,否则会造成死循环

# C语言模块化编程概念

##1.模块化编程的概念

- 在编写第一个C程序的时候已经提到:我们编写的所有C语言代码都保存在拓展名为.c的源文件中,编写完毕后就进行编译、链接,最后运行程序。

- 在前面的学习过程中,由于代码比较少,因此所有的代码都保存在一个.c源文件中。但是,在实际开发过程中,项目做大了,源代码肯定非常多,很容易就上万行代码了,甚至上十万、百万都有可能。这个时候如果把所有的代码都写到一个.c源文件中,那么这个文件将会非常庞大,你可以想象一下,一个文件有十几万行文字,不要说调试程序了,连阅读代码都非常困难。

- 而且,公司里面都是以团队开发为主,如果多个开发人员同时修改一个源文件,那就会带来很多麻烦的问题,比如张三修改的代码很有可能会抹掉李四之前添加的代码。

- 因此,为了模块化开发,一般会将不同的功能写到不同的.c源文件中,这样的话,每个开发人员都负责修改不同的源文件,达到分工合作的目的,能够大大提高开发效率。也就是说,一个正常的C语言项目是由多个.c源文件构成。

- 所谓模块化编程(多文件开发),就是多文件(.c文件)编程,一个 .c 文件和一个 .h 文件可以被称为一个模块。

- 如果你学习C语言很久了,还只会在一个.c文件里写代码,那么今天将会让你突破,带你了解多文件编程,教你学会如果有效的组织各个文件,如果将各个文件联系起来。

>- 在以下场景中会使用头文件:

+通过头文件来调用库功能。

* 在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。

+多文件编译。

* 将稍大的项目分成几个文件实现,通过头文件将其他文件的函数声明引入到当前文件。

+头文件能加强类型安全检查。

* 如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,

这一简单的规则能大大减轻程序员调试、改错的负担。

##2.多文件开发实现

- 运行步骤分析:

- 在编译之前,预编译器会将sum.h文件中的内容拷贝到main.c中

- 接着编译main.c和sum.c两个源文件,生成目标文件main.o和sum.o,这2个文件是不能被单独执行的,原因很简单:

* sum.o中不存在main函数,肯定不可以被执行

* main.o中虽然有main函数,但是它在main函数中调用了一个sum函数,而sum函数的定义却存在于sum.o中,因此main.o依赖于sum.o

- 把main.o、sum.o链接在一起,生成可执行文件

- 运行程序

- 有人可能觉得分出sum.h和sum.c文件的这种做法好傻,好端端多出2个文件,你把所有的东西都写到main.c不就可以了么?

+没错,整个C程序的代码是可以都写在main.c中。但是,如果项目做得很大,你可以想象得到,main.c这个文件会有多么庞大,会严重降低开发和调试效率。

+要想出色地完成一个大项目,需要一个团队的合作,不是一个人就可以搞的定的。如果把所有的代码都写在main.c中,那就导致代码冲突,因为整个团队的开发人员都在修改main.c文件,张三修改的代码很有可能会抹掉李四之前添加的代码。

+正常的模式应该是这样:假设张三负责编写 main函数,李四负责编写其他自定义函数,张三需要用到李四编写的某个函数,怎么办呢?李四可以将所有自定义函数的声明写在一个.h文件中,比如 lisi.h,然后张三在他自己的代码中用#include包含lisi.h文件,接着就可以调用lisi.h中声明的函数了,而李四呢,可以独立地在另外一个文件中(比如lisi.c)编写函数的定义,实现那些在lisi.h中声明的函数。这样子,张三和李四就可以相互协作、不会冲突。

##3.多文件开发的注意事项

- 1)头文件中可以和C程序一样引用其它头文件,可以写预处理块,但不要写具体的语句。

- 2)可以声明main函数,
但不可以定义函数

- 3)可以声明常量, 当不可以定义变量

- 4)可以“定义”一个宏函数。注意:宏函数很象函数,但却不是函数。其实还是一个声明。

- 5)结构的定义、自定义数据类型一般也放在头文件中。

- 6)多文件编程时,只能有一个文件包含main() 函数,因为一个工程只能有一个入口函数。我们把包含 main() 函数的文件称为主文件。

- 7)为了更好的组织各个文件,一般情况下一个 .c 文件对应一个 .h 文件,并且文件名要相同, 例如 fun.c 和 fun.h。

- 8)头文件要遵守幂等性原则,即可以多次包含相同的头文件,但效果与只包含一次相同。

- 9)防止重复包含的措施

# \#include指令

##1.include基本概念

- \#include 是C语言的预处理指令之一,所谓预处理,就是在编译之前做的处理,预处理指令一般以 # 开头

- \#include 指令后面会跟着一个文件名,预处理器发现#include 指令后,就会根据文件名去查找文件,并把这个文件的内容包含到当前文件中。被包含文件中的文本将替换源文件中的 #include 指令,就像你把被包含文件中的全部内容拷贝到这个 #include 指令所在的位置一样。所以第一行指令的作用是将stdio.h文件里面的所有内容拷贝到第一行中。

- 如果被包含的文件拓展名为.h,我们称之为"头文件"(HeaderFile),头文件可以用来声明函数,要想使用这些函数,就必须先用 #include 指令包含函数所在的头文件

- \#include 指令不仅仅限于.h头文件,可以包含任何编译器能识别的C/C++代码文件,包括.c、.hpp、.cpp等,甚至.txt、.abc等等都可以

##2.文件包含的格式

当包含我们自己写的文件就是使用 #include "" 当包含系统提供头文件的时候,就是用#include<>

- 示例:

如:

#include <stdio.h>

#include <math.h>

#include "one.h"

- 注意:include 语句之后不需要加";"(因为#include它使一个预处理指令,不是一个语句)

##2.#include <>和#include ""的区别

- 二者的区别在于:当被include的文件路径不是绝对路径的时候,有不同的搜索顺序。

- 对于使用双引号""来include文件,搜索的时候按以下顺序:

+ 先在这条include指令的父文件所在文件夹内搜索,所谓的父文件,就是这条include指令所在的文件

+ 如果上一步找不到,则在父文件的父文件所在文件夹内搜索;

+ 如果上一步找不到,则在编译器设置的include路径内搜索;

+ 如果上一步找不到,则在系统的include环境变量内搜索

- 对于使用尖括号<>来include文件,搜索的时候按以下顺序:

+ 在编译器设置的include路径内搜索;

+ 如果上一步找不到,则在系统的include环境变量内搜索

如果你是自己安装clang编译器,clang设置include路径是(4.2是编译器版本):

/usr/lib/clang/4.2/include

Xcode自带编译器, clang设置include路径是

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include

Mac系统的include路径有:

/usr/include

/usr/local/include

如果没有这个目录,可参考如下:

打开终端输入:xcode-select --install

安装Command Line Tools之后就会出现

##3.include注意事项

- include 不一定非要写在第一行

int main()

{

#include "b.h"

return 0;

}

- include的时候,可以包含路径。

#include "text/b.h"

int main()

{

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: