一、【理解】复习昨天内容
二、【掌握】函数的介绍和简单使用
思考:
如果你要做个表白的软件,其中想打印9次爱心,代表你对妹纸浓浓的爱,怎么做呢?
亲!你需要一种更简单高效的编码方式——函数!!
思考:
我们已经学过的函数有哪些?
函数的作用:
函数就相当于是一个打包盒,将一段代码打包在这个盒子里,当需要用这段代码时,只要把这个盒子拿过来就行了。
稍微专业点的说法:
将一段需要被重用的代码封装在函数中,什么时候想要执行这段代码,只需要调用这个函数就行了。
函数的定义:
返回值类型 函数名(参数列表){
函数体
}
例:
int main(){
}
void test(){
}
```
####函数定义的注意点:
#####1、函数名可以任意写,但最好符合标识符命名规范:
1)函数的名称一定要有意义,尽量做到别人一看你函数名就知道你的函数内部做什么事情
2)函数的名称用驼峰命名法:如果有多个单词,第一个单词首字母小写,后面的其他单词字母首字母大写
#####2、什么时候该使用函数?
如果你发现有1段代码是在完成1个独立的功能或者需要重用,那么这个时候我们也可以将这段代码封装在函数之中.
#####3、函数的好处:
1、减少代码冗余,增强阅读性
2、方便维护和修改
#####练习:使用函数来打印九九乘法表
###三、【掌握】Xcode调试的使用
####一、断点调试
![断点调试](http://upload-images.jianshu.io/upload_images/3899701-4cb79f263456856b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![断点示意图](http://upload-images.jianshu.io/upload_images/3899701-de4cb1b007189208.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
下断点有什么用?
当你用Xcode编译并启动程序时,程序执行到你下断点的地方会卡住,不再往下执行,如:
![如图](http://upload-images.jianshu.io/upload_images/3899701-56eb3e4a85fb46da.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我们发现第43行代码处有一行绿色的yin阴影,这代CPU执行到这一行时停止了(此行还没执行),如果想继续往下执行,此时有三种方法:
#### 1、往下继续执行全部
继续往下执行,直到遇到下一个断点、阻塞式函数、或者程序结束
![如图所示](http://upload-images.jianshu.io/upload_images/3899701-2a64d4050acd667b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
调用完成直接执行后面的结果,打印图形和文字
![打印图形](http://upload-images.jianshu.io/upload_images/3899701-adf8002753cdd70d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
####单行跳过执行(step over)
一行一行地执行,按一次执行一次。但是使用这种模式会使当前遇到的函数一下子就执行完
![单行跳过执行](http://upload-images.jianshu.io/upload_images/3899701-be7e46bbdfb0bf58.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![如图所示](http://upload-images.jianshu.io/upload_images/3899701-77aa28dccacb8fe3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
按完后会执行完printf,停在drawHeart
![如图](http://upload-images.jianshu.io/upload_images/3899701-3a01c0356044e94b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![图2](http://upload-images.jianshu.io/upload_images/3899701-dee59eeeb7d4f73e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
####3、单行进入执行(step into)
一行一行地执行,按一次执行一次。但是使用这种模式会使进入到当前遇到的函数,并在函数里一步一步运行
![单行进入](http://upload-images.jianshu.io/upload_images/3899701-3fb2c1d2f7db69e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
此时CPU卡在drawHeart,当我们再按step into时,发生了和step over不同的事情
![如图所示](http://upload-images.jianshu.io/upload_images/3899701-94d426d6602de4d9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![图2](http://upload-images.jianshu.io/upload_images/3899701-dcddbf064f414144.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![图3](http://upload-images.jianshu.io/upload_images/3899701-8437fe0c2fd89c80.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![图4](http://upload-images.jianshu.io/upload_images/3899701-c3754491ffd0e4e2.png)
![图5](http://upload-images.jianshu.io/upload_images/3899701-d87fa98ab3e081e8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
总结:
step over 和step into最大的不同在于:
step over一下子就执行完函数,不进入函数的内部
step into 会进入到函数的内部,在内部一行一行地执行
###二、删除断点:
1)直接按住断点左键不动往右边一拖再松开即可删除断点
2)右键断点阴影,在弹出的选项中左键单击Delete Breakpoint即可删除断点
![如图所示](http://upload-images.jianshu.io/upload_images/3899701-67132365cfae7784.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
四、【掌握】全局变量和局部变量
-> 全局变量
直接定义在.c源文件中,函数的外部.就叫做全局变量
-> 局部变量
定义在函数内部的变量,就叫做局部变量
####不同点:
1)全局变量可以在任何函数中使用,而局部变量只能在被申明这个变量的函数里使用。
2)全局变量直到程序结束时才会销毁,而局部变量在函数结束后就会被销毁
### 五、【掌握】函数的参数
思考:
如果想做一个函数,用来判断一个数是不是偶数,怎么办?
我们发现,很多时候函数要想完成某种功能,也必须要接收外界传入的值才能进行处理,那么这时怎么办呢?
我们可以使用全局变量解决这个问题,但是,全局变量有个很不好的地方在于始终占据着我们的内存空间,除非程序退出才会释放占用的空间,那么这时怎么办呢?
好在C语言为我们提供了一套十分好的解决方法:函数的参数!
####1、什么是函数的参数?
在函数名右边的小括号里申明的变量,就叫参数。注意:没有分号!
####2、函数的参数的本质:
相当于函数内部的局部变量,所以不能在函数内部再定义一个同名的变量,并且在函数内部可以直接使用参数的值
![函数本质示意图](http://upload-images.jianshu.io/upload_images/3899701-1bd2e09561ad5300.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
调用函数时需要给参数进行赋值,否则会报错!如:
![函数本质示意图2](http://upload-images.jianshu.io/upload_images/3899701-05b3c841d88443ce.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
思考:什么时候该为函数写参数呢?
建议:如果以后不知道要不要给函数写参数,就先写函数的功能,等发现需要接收外部传入的值时,再写函数!
####3、带多个参数的函数
思考:如果我们需要做一个实现做加法运算功能的函数,怎么做呢?
我们再一次发现很多时候一个参数是完全不够用的,所以我们需要多个参数。多个参数怎么定义
呢?
定义方法:
返回值 函数名(参数类型 参数名1,参数类型 参数名2…………参数类型 参数名n){
函数体;
}
使用注意:
1)实参的个数必须与形参的保持一致
2)实参的顺序也必须与形参的保持一致
3)实参的类型必须与形参保持一致,否则会做类型转换
![如图所示](http://upload-images.jianshu.io/upload_images/3899701-7cbff79eaff2096e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
###4、形参和实参
形参:全称形式参数。在声明函数右边的小括号里写的参数就叫形参。
实参:全称实际参数。在调用函数时,给函数的形参赋值的值就叫实参。
![形参和实参](http://upload-images.jianshu.io/upload_images/3899701-5bd6477b091d20da.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
使用注意:因为实参的本质就是在调用函数时为形参赋值,所以当我们调用一个带参数的函数时,会要求实参的类型与形参的类型一致。如果类型不一致,会将实参的数据类型转换为形参的类型然后再赋值
例: int
a = 10.3 会先将10.3转成int类型(即10)再赋值给变量a,所以变量a的值为10
在函数的实参中,也是类似这样的过程来给形参赋值
###六、【理解】课堂练习
1、用函数来实现判断一个数是不是水仙花数
2、写一个函数,求指定某个数到某个数之间所有的整数的累加和
如:3到8,算出3+4+5+6+7+8 = ?
3、用函数来实现判断一个年份是不是闰年(考点)
###七、【掌握】return关键字
回忆:break 、 continue关键字的作用是什么?
return关键字也有结束的作用,但效果比break、continue恐怖的多,是直接结束整个函数。return关键字后面的语句不会被执行
总结:break continue return
break:用在循环或者switch case语句中,用来结束其所在的循环或者switch
case语句
continue:用在循环中,用来结束其本次循环,继续下次循环
return:用在函数中,用来结束整个函数
练习:
用函数实现判断一个数是不是偶数,但函数内部只能用if语句实现,不能用if else 或者if else if
###八、【掌握】函数的返回值
思考:写一个函数实现计算出语文成绩和数学成绩的总分。然后苍老师叫小明和小王还有小军分别去计算出他们的总分,然后苍老师要统计他们三个人的总分,这时怎么做呢?
####什么时候需要函数的返回值?
当函数执行完毕能得出一个结果,但是函数内部不知道怎么正确地处理这个结果,恰好这个结果又是调用者想用来得到的,就需要有函数的返回值!
####怎样让函数有返回值?
1)确定要返回的值的类型
2)修改函数的返回值类型为你想要返回值的类型(如将之前的void改为int)
3)在函数内使用 return
表达式
返回
####调用者如何拿到函数返回的数据?
申明一个与函数返回值相同类型的变量,并将返回值赋值给这个变量就可以了
####函数的其他注意点:
1)如果函数有返回值,调用者可以接收,也可以不接收。但如接收,需要准备对应的类型来接收
2)void 代表没有返回值,如果一个函数返回值是void的,调用者不需要接收。如果强行接收会报错
3)返回的数据类型要与函数定义的返回值类型一致,如果不一致,会先将所返回的值转换成定义的返回值类型再返回
4)如果函数定义了返回值,并且函数体里面有多个分支,那么每个分支都必须要有返回值,否则会报错
###九、【掌握】函数的申明
####1、为什么需要对函数进行申明
编译器很笨,只会从上到下依次将代码翻译成计算机可以识别的二进制指令,所以如果当函数的申明写在它被调用之后,编译器会报错!
为了解决这个问题,所以如果函数的实现写在调用语句后面的话则需要先对函数进行申明。代表告诉编译器:有这个函数,你只管编译就好!
####2、 完整的写函数的两个步骤
1). 函数的声明
语法: 只写函数头 直接加1个分号结束.
作用: 告诉编译器,这个函数是有的 只不过在后面.
2). 函数的实现(定义)
和我们之前讲的函数的定义是一样的.
表示真正的来实现这个函数.
####3、函数申明的几点注意
1). 如果被调用的函数在主调用函数之前,那么被调用函数可以不写声明直接写实现
一般情况下,main函数是放在最前面的. 所以我们自定义的函数要在最前面写声明,在后面写实现.
![注意](http://upload-images.jianshu.io/upload_images/3899701-61c0772b73a802e8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
2). 函数的声明可以在任意的地方但是至少应该在函数调用之前声明
即使是这样. 但是一般情况下 函数的声明我们都放在#include指令之后.
3). 函数的声明必须要与函数实现的函数头一样,否则会出问题.
所以,我们建议大家 先写函数的实现 然后将函数实现的头拷贝声明.
![如图](http://upload-images.jianshu.io/upload_images/3899701-b1220432c540f710.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
4). 声明函数的时候形参可以只写类型不写名称
![如图4](http://upload-images.jianshu.io/upload_images/3899701-8a6b7b91fe8633e4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
5). 如果函数的返回值类型是int类型的那么可以省略函数声明
但是为了更好的代码可移植性 以及可阅读性十分的建议还是要把声明写上.
![如图](http://upload-images.jianshu.io/upload_images/3899701-df9ca0ab6918ed19.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
###十、【掌握】函数的其他注意
1、值传递:是指实参给形参赋值,传递的永远是值,而非实参的内存空间(以后学指针后可以把空间地址传过去),所以在函数里给形参重新赋值不会影响到实参。
2、C语言中函数没有重载。即:C语言中的函数不允许出现函数同名的情况,否则编译器会报错
3、函数内部不能定义一个新的函数,但是可以在函数内部再调用函数
###十一【理解】函数练习
1. 写1个函数 返回两个整型数中的最大值.
2. 写1个函数,传入1个人的年龄,判断年龄是否成年
附加题:
1. 写1个函数,返回1个浮点数的四舍五入的整数
2.写1个函数,用来判断3个整数的最大值,这个函数只能传两个参数
即申明成右边的格式:int getMax(int num1,int num2);
###十二【理解】#include指令
####1、#include是一个预处理指令
#####1) 什么是与处理指令?
就是在源代码编译之前进行的一系列操作。其中预处理指令包含:文件包含、宏定义、条件编译指令 等等
预处理指令的特点:
A、所有的预处理指令都是以#开头
B、没有分号
C、在编译之前执行
##### 2)#include指令是一条文件包含预处理指令
作用:将指定文件的内容拷贝到写这条#include指令的地方
语法:
include "文件名称"
include <文件名称>
例:
![ 预编译](http://upload-images.jianshu.io/upload_images/3899701-86c8eb3bcf224c83.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
使用注意:
1)被包含的文件里面的内容必须符合C语言语法规范,否则即使包含进来,编译的时候也会报错!
![图1](http://upload-images.jianshu.io/upload_images/3899701-48e605a04d888e15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
2)如果要包含的文件就在当前写#include指令文件的同一目录,则包含路径可以省略,只写文件名。
例:
![图2](http://upload-images.jianshu.io/upload_images/3899701-2ec54210acc01cc6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
###2、#include指令的搜索顺序
分两种情况:
1)#include
"文件名称"
a、如果指定了全路径,则直接去对应的路径查找文件
b、如果没有指定全路径,则先去当前代码文件所在文件夹里查找,如果有就包含,没有的话就会再去Xcode的编译器目录下去查找是否有这个文件,如果有则直接包含进来,如果这时还没有,则报错
Xcode编译器目录
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include
2)#include <文件名称>
直接去Xcode编译器目录去查找这个文件,如果有就包含进来,如果没有就报错
###3、 #include<stdio.h>的分析
我们学过函数后知道,如果要调用某个函数,必须在调用之前对这个函数进行申明,而之前我们所用的printf("") scanf("")都是函数,只不过这些是系统给我们写好的函数,如果没有进行申明直接调用会报错,
所以我们需要对其进行申明,而这些函数申明的内容,都包含在苹果为我们提供的Xcode编译器里的各种文件里了,我们只要用#include指令包含进来就行了!
###4、#include规范
如果是我们自己写的文件,放在代码文件里,并且用""包含
如果是编译器提供的头文件,则用<>包含
###十三【掌握】多文件开发
思考:实现微信摇一摇有哪些功能步骤?
我们实际开发中的项目,基本都是团队协作开发,每人负责一个模块(功能),这样比较利于项目的快速开发。
案例:
模块:数学功能
将1个功能模块的函数应该单独的写在1个文件之中. 每1个模块1个文件.
新建1个.c文件.在这个.c文件之中写上相关的函数. 在要调用的地方, 先声明函数以后才能调用.
这么做可能存在的问题:
1). 如果写模块的人 新增了1个函数 那么调用者就还要去声明这个函数.
2). 如果写模块的人 改了函数不通知调用者的话 也会出问题.
正确的做法:
1). 写模块的人写两个文件 .c文件 .h文件. header头文件.
.h文件中写这个模块中的函数的声明.
.c文件中写这个模块中的函数的实现.
2). 调用模块功能的人 只需要包含头文件就可以 调用模块中的函数.
4. 模块.
1个模块至少包含2个文件
.h文件
.c文件
###十四【理解】抽查问题
1、为什么需要使用函数?
2、函数的基本格式
3、全局变量和局部变量有什么不同?
4、return、break、continue的异同
5、给形参重新赋值会影响实参吗
6、函数内部可以再定义一个函数吗?
7、函数可以重名吗?
8、#include的本质是什么?
9、#include后面使用""和使用<>有什么不同?