C是一门结构化的编程语言,Objective-C是C的超集。
为什么要定义函数?
为了方便复用某段代码,可以将这段代码定义成一个函数,以后每次调用该函数,就相当于让程序去执行这段代码。
定义函数(function)的语法
返回值类型 函数名(形参列表)
{
}
返回值类型:可以是任何有效的类型(基本类型、构造类型、引用类型)。
所谓返回值类型,决定了该函数执行成功之后,所返回的结果的类型。
因此如果没有返回值类型,应该将返回值类型声明为void。
如果不写返回值类型,表明返回值类型为int。
如果声明了返回值类型(包括不写),程序应该在函数体使用return语句来返回函数的返回值。
函数名:标识符即可。
实际上,函数名应该一个或多个有意义的单词连缀而成。
形参列表: 形参类型 形参名, 形参类型 形参名, ...
每个形参都必须由 形参类型和形参名所组成。
调用函数
注意点:
1. 声明函数时指定了几个形参,调用函数时就需要传入几个参数、而且参数的类型要一致。
2. 函数的返回值,既可用变量去装;也可立即拿来用掉(将函数返回值放入表达式中)。
函数声明
C语言是从上到下的,默认要求:函数必须先定义、再使用。
对于如下两种情况:
- 函数定义在函数调用处的后面。
- 函数定义在另一个源文件中。
此时就需要使用函数声明 —— 函数声明就是告诉编译器:这个函数是存在的。
函数声明:就是重要函数的返回值类型、函数名、形参类型即可,不要函数执行体。
注意:函数声明时既可有形参名,也可没有形参名。
import语句,就是导入大量的函数声明。
由于函数声明必须在所有函数调用的前面,因此程序就需要将#import语句放在程序在最上面。
函数的传参机制
Object-C的传参机制是“值传递”,程序传入函数的只是参数本身的副本(复制品),
因此函数所参数所作的修改,对参数本身是没有任何影响的。
注意:对于传递指针的情形,实际上传递的依然是指针的复制品(副本)。
递归函数
如果一个函数,调用了自身,该函数就会形成递归函数。
递归,可实现一个隐式的循环。
与循环类似的是,递归也必须有结束的时候。
递归:一定要有结束的时,要向已知的方向递归。
使用数组本身(数组的本质就是指针)作为参数
注意:
1. 当在函数中声明数组类型的参数时,既可指定数组的长度,也可不指定数组的长度,但通常都不会指定。
2. 由于数组本身是一个指针,因此传入函数的并不是数组本身,只是一个指针的副本。
因此函数无法通过sizeof来获取被传入的数组的长度。
3. 由于数组本身是指针,因此函数对数组元素所做的修改会影响数组本身的元素的。
内部函数与外部函数
C语言是结构化程序设计语言,因此函数是C的一等公民:
一个C程序通常是由于很多个函数组成,程序员开发的函数,可能还需要依赖系统内置的函数。
由此可见:C语言程序,可能由成千上万的函数组成,但最多只能有一个main函数
—— 它作为程序的入口,其他函数都是直接或间接地被main函数调用,所有函数的地位是平等。
每个函数都可调用别的函数,也可被别的函数调用。
static修饰符的作用之一:把外部东西(函数、全局变量),变成内部东西(函数、全局变量)。
- static函数,就是内部函数——只能在当前源文件中被调用。
- 没static修饰或extern修饰的函数,可以任何地方被调用(只要先做函数声明即可)。
对于内部函数,程序只有通过直接把源代码import进来才可以调用内部函数。
而外部函数,程序即使拿不到定义函数的源代码,只要有定义函数的源代码生成的库,程序也可通过函数声明之后来调用外部函数。
局部变量与全局变量
C的变量分为:
局部(local)变量:在函数里、方法里定义的变量,都是局部变量。
局部变量保存在各自的栈区中,函数、方法执行结束时,栈区被销毁。
特征:只在定义该变量的函数、方法内有效,函数、方法执行完,局部变量立即消失。
全局(global)变量:在文件范围内定义的变量,都是全局变量。
全局变量保存在整个程序的内存(静态存储区)中。程序不结束,静态存储区不会消失。
特征:只要程序不退出,全局变量将一直有效。
细分局部变量,又可分为:
- 形参:从函数被调用开始,形参开始生效,函数结束时,参数失效。
- 方法里局部变量:从定义该变量开始生效,函数结束时,该变量失效。
- 代码块的局部变量:从定义该变量开始生效,代码块结束时,该变量失效。
包括在if条件体、循环体、for循环的初始化语句中定义的变量,都属于代码块的局部变量,离开了定义该变量的花括号区域,那么该变量就失效了。
细分全局变量
- static全局变量,就是内部[全局]变量——只能在当前源文件中被调用。
- 没static修饰的全局变量,可以任何地方被调用(只要先做变量声明即可)。
变量声明:如果程序要使用其他源程序中定义的全局变量时,就必须做变量声明。语法是:
extern 类型变量名;
局部变量的存储机制:
定义局部变量时,有如下几个修饰符:
auto - 有auto和没auto效果是一样的。也就是说,你定义的所有的局部变量,默认就相当于有个auto。
auto修饰的局部变量,会被保存到函数或方法的栈区中。
static - 把局部变量放到静态存储区中。
对于static的局部变量而言,只要程序不退出,静态局部变量将一直有效。
由于static局部变量会一直有效,因此程序只有第一次调用函数时,才会对static局部变量赋初始值。
register - 控制把局部变量放入寄存器中。
但OS并不保证register变量一定会进入寄存器,所以实际上这个修饰用的并不多。
关于定义变量的原则:
1. 程序应该优先使用局部变量(全局变量是非常糟糕的设计、会造成牵一发而动全身的效果)。
2. 如果局部变量需要能在函数、方法结束时,依然可以保存上次运行的数据,或者该变量只希望第一次调用时被初始化,
该局部变量应该用static修饰。
3. 实在没办法,才考虑使用全局变量。
典型的,当一个程序单元需要把数据共享给另外一个程序单元时,正确做法是参数传递,而不是全局变量。
static修饰符有2个作用:
- 修饰全局函数、全局变量,使之变成内部函数、内部全局变量。
- 修饰局部变量,使之成为静态局部变量——静态局部变量会被保存到程序的静态存储区。
extern修饰符也有2个作用:
- 声明全局变量。
- 修饰外部函数。————但实际上,不用extern效果也是一样的。
预处理
预处理:程序在编译阶段就会处理调用东西,这些预处理指令不会保留到运行阶段。
对于源程序中包含的预处理,编译器会在编译阶段处理完成,到运行时就不存在预处理指令。
预处理的特征:
- 通常在程序的开头。
- 必须以#开始。
#define 或 #undef定义宏变量和取消宏
#define定义的又被称为:“宏(macro)变量”。
#define的作用是:编译器在处理程序之前,先执行查找、替换。
通过#define定义宏变量有两个好处:
- 可以方便程序以后修改。
- 可以提高程序的可读性。
#undef:取消宏定义
#define还可以定义带参数的宏
对于带参数的宏定义而言,有两点要注意:
- 定义宏的参数时,并不是定义函数的参数,因此宏的参数无需指定参数类型。
- 使用宏的参数时,一定要参数放在圆括号中。
使用#ifdef #ifndef #else #endif执行条件编译。
它的用法基本上和前面介绍的分支是一样的。
它们的主要作用是判断指定的宏是否存在,根据指定宏是否存在来执行相应的分支。
使用#if #elif #else#endif执行条件编译。
预处理的if分支比普通的if分支的效率要高的多。
能用预处理的if分支,应该尽量使用预处理的if分支。
记住:只要你的if条件中包含了变量、函数调用等表达式,就只能使用普通的if分支。
#include和#import
#include和#import的功能是类似的,但#import的功能更强大一些。
#import就可以自动避免重复include导致的错误。