创建C函数
#include
1、create OS X中的Command Line Tool 并且选择C生成main.c文件
2、在Xcode5中创建OS X中C and C++中的C File
在Xcode6中创建OS X中Source中的C File
#import
1、在Xcode5中create OS X中的Command Line Tool 并且选择Foundation生成main.m文件
在Xcode6中create OS X中的Command Line Tool 并且选择Objection-C生成main.m文件
2、在Xcode6中创建OS X中Source中的Objection-C File
定义函数:先定义函数,在调用函数—>函数定义位于函数调用的前面
定义函数的语法格式:函数返回值类型 函数名(形参列表)
{
//由零条到多条可执行性语句组成的函数;
}
例如:
void initBoard(){}
(3)形参列表:用于定义该函数可以接受的参数,形参列表由零组到多组“参数类型 参数名”组合而成,多组参数之间以英文字符(,)隔开,形参类型和形参名之间以英文空格隔开。
注意:
(1)如果声明函数时指定的返回值类型与return语句实际返回的数据类型不匹配,此时将以声明函数时指定的返回值类型为准,系统将把return实际返回的值转换成声明函数时指定的类型。
(2)如果被调用函数没有retuen语句,该函数并不是真正没有返回值,它只是返回一个不确定的,不一定有用的值。因此,如果我们希望一个函数没有返回值,一定要明确的用void来声明没有返回值。
函数声明:函数声明来指定该函数的形参类型和返回值类型—>被调用函数位于后面或者函数定义在另一个源文件中;
函数声明形式:
(1)、只声明函数的返回值类型、函数名、形参列表的形参类型,不保留形参名;
例如:
void printMsg(NSString *,int);
(2)、声明函数的返回值类型、函数名、完整的形参类型,包括形参名;
例如:
void printMsg(NSString *msg,int loopNum);
提示:对于第二种形式,实际上就是函数定义部分做的函数体(花括号及花括号里的所有代码)部分舍弃剩下的部分,并在后面添加一个分号。
3、函数的参数传递机制:如果声明函数时包含了形参声明,则调用函数时必须给这些形参指定参数值,调用函数时,实际传给形参的参数值也称为实参。
递归函数:函数体调用自身。
例如:
int fn(int n){
if (n == 0) {
return 0;
}else if(n == 1){
return 1;
}else{
return 2 * fn(n - 1) + fn(n - 2);
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"%d",fn(10));
}
return 0;
}
(2)、当数组作为函数的参数时,声明函数的形参类型与调用函数时传入的实参类型必须保持一致。
例如:
double avg(int array[]){}
内部函数:定义函数时使用static修饰,改函数只能被当前源文件中的其他函数所调用。
优点:具有更好地内聚性,他可以保证函数只有在该源文件中起作用,从而避免了多个源文件总重名函数的冲突问题。
外部函数:定义函数时使用extern修饰,或不适用任何修饰符修饰。它可以被任何源文件中的函数调用。
例如
在PrintFunctionLib.m文件中
#import
void printRect(int height,int width){
//控制打印height行
for (int i = 0; i < height; i ++) {
//控制每行打印width个星号
for (int j = 0; j < width; j ++) {
printf("*");
}
printf("\n");
}
}
//定义外部函数
void printTriangle(int height){
//控制打印height行
for (int i = 0; i < height; i ++) {
//控制打印height - 1 - i个空格
for (int j = 0; j < height - 1 - i; j ++) {
printf(" ");
}
//控制打印2*i + 1个星号
for (int j = 0; j < 2 * i + 1; j ++) {
printf("*");
}
printf("\n");
}
}
在main.m文件中
#import
//声明两个外部函数
void printRect(int,int);
void printTriangle(int);
int main(int argc, const char * argv[]) {
@autoreleasepool {
//调用两个函数
printRect(5, 10);
printTriangle(7);
}
return 0;
}
局部变量:在函数内部定义的变量,只在该函数内部有效,只在该函数内部才能使用它们,在函数外部无法访问这些变量。
全局变量:定义在函数外部的变量,可以被源文件中的所有函数访问,作用域:从定义该函数的位置开始,到该源程序结束。
根据定义形式不同,分三种
注意:
(1)在一个函数内部,如果全局变量和局部变量同名,局部变量将会覆盖全局变量,这就意味着在该函数内部,全局变量将会失效。
(2)任何函数对全局变量所做的修改,其它函数都会受到影响,其它函数读取全局变量的值时,也是被修改后的值。
外部全局变量:C语言中允许访问其他源程序中定义的全局变量,允许被其他源程序访问的全局变量称为外部全局变量。
内部全局变量:使用static修饰的全局变量被称为内部全局变量。
注意:在使用外部全局变量的时候,需要用extern声明
例如:
在PrintFunctionLib.m文件中
#import
//定义全局变量
int count = 0;
//定义一个函数
void change(){
NSLog(@"count的值为:%d",count);
count = 20;
}
在main.m文件中
#import
//声明外部函数
void change();
int main(int argc, const char * argv[]) {
@autoreleasepool {
//声明外部局部变量
extern int count;
change();//调用外部函数
NSLog(@"%d",count);
count = 50;//对外部变量赋值
change();
}
return 0;
}
输出:
count的值为:0
20
count的值为:50
(1)、C程序运行内存来说:分为3部分:程序区、静态存储区和动态存储区。
(2)、C语言支持的几种存储类型:
注意:
(1)计算机中寄存器的数量是有限的,不能定义任意多个寄存器变量。
(2)不同的系统对register局部变量的处理是不同的,许多系统并不会真正的把register局部变量存入寄存器,他们依然把register局部变量当成auto局部变量处理。
特征:
(1)、预处理命令必须以#开头。
(2)、预处理命令通常位于程序开头部分。
#define 的作用就是为字符串起一个名字,且一般使用所有字母大写的形式。
例如:
#define PI 3.1415926
#define TEO_PI PI * 2
注意:
(1)、宏定义并不是C语句,因此不用在宏名称与字符串之间使用=进行赋值,而且宏定义也无需使用分号结束;
(2)、宏定义不是变量,他甚至不是常量,因此不能尝试对宏名称进行赋值;
(3)、编译器处理宏定义只是进行“查找、替换”——将所有出现宏名字的地方替换成该宏对应的字符串,要保证宏定义是正确地。
PS:如果希望提前结束宏定义则使用下面语句:
#undef PI
定义参数宏的语法:#define 宏名称(参数列表) 字符串
#define PI 3.1415926
#define GIRTH(r) PI * 2 * r//直接使用前面已有的PI来定义新的宏
此时需注意:#define GIRTH(r) PI * 2 * (r)
C语言支持两组条件编译指令
第一组条件编译指令:#ifdef、#ifndef、#else、#endif
语法格式:
#ifdef 宏名称
//任意语句
#endif
表示:如果定义指定宏,则执行#ifdef和#endif之间的语句
#ifdef 宏名称
//任意语句
#else
//任意语句
#endif
表示:如果定义了指定宏,则执行#ifdef和#else之间的语句;否则执行#else和#endif之间的语句。
//任意语句
#endif
表示:如果没有定义指定宏,则执行#ifdef和#endif之间的语句
#ifndef 宏名称
//任意语句
#else
//任意语句
#endif
上面表示:如果没有定义了指定宏,则执行#ifdef和#else之间的语句;否则执行#else和#endif之间的语句。
作用:
(1)、源程序可以不必做任何修改就可自动适应不同的设备;
(2)、控制是否输出调试信息。
例如:
第一种:
#ifdef DEBUG
# define DLog(...) NSLog(__VA_ARGS__)
#else
# define DLog(...) /* */
#endif
#define ALog(...) NSLog(__VA_ARGS__)//定义输出
第二种:
#ifndef __OPTIMIZE__
# define debug 1
# define NSLog(...) NSLog(__VA_ARGS__)
#else
# define debug 0
# define NSLog(...) {}
#endif
第二组条件编译指令:#if、#elif、#else、#endif
语法格式:
#if 表达式
//任意语句
#elif 表达式
//任意语句
//可以有零个或者多个#elif语句
//最后的#else也可以省略
#else
//任意语句
#endif
注意:表达式中的条件要么是常量表达式,要么就是基于已有宏的表达式。
C语言:提供了#include来导入其他源程序;
OC语言:提供了#import来导入其他源程序;
#include作用:将指定的源代码插入到当前源代码中。
#import:
(1)、一般情况下导入自定义的源程序都是使用双引号来包含源文件,这告诉预处理程序将会到一个或多个路径下(通常首先搜索当前文件所在路径,也可通过Xcode的项目设置来设置预处理程序的搜索路径)搜索指定的源文件。
例如:
#import "RootViewController.h"
(2)、如果将要包含的源程序放在<和>之间,用于告诉大家系统只在特定的“系统”头文件路径中搜索被导入的文件,而不再当前路径搜索。
#import
总之:
如果导入用户自定义的源文件,在#import后使用双引号来包含源文件的文件名;
如果要导入系统的源文件,则在#import后使用<和>来包含源文件的文件名。
格式:类型* 变量名;
注意:*代表一个指针变量,整个语法代表定义一个指向类型变量的指针变量,指针变量不能保存普通的值,只能保存指针(也就是变量的对象);
基本运算符:(*(&a))== a;
注意:
(1)、如果使用函数参数是普通类型来的变量,函数中对变量所做的任何修改都不会影响变量本身。
(2)、如果程序需要在函数中对变量本身的值进行修改,则需要将变量的指针(即地址)传入函数,并在函数中对该指针所做的变量进行修改,这样既可改变原来本身的值。
提示:将数组变啦ign作为参数传入函数时,在函数中对数组元素所做的修改也会影响原数组元素的值,这是因为数组变量本身也是指针。
首地址:数组中的第一个元素的地址被称为数组的首地址,数组的首地址会被当成数组的地址。C语言规定,数组变量的本质就是一个指针常量,保存了指向第一个数组元素的指针。
获取数组首地址的方式:
指针变量存在的几种赋值方式:
将数组变量作为参数的本质:将指针变量作为参数。
当把数组变量作为参数传入函数时,只是把该数组变量的值(指向数组的指针)传入函数,并不是将数组本身传入函数,因此,传入函数的数组变量依然指向所有的数组。在函数中对数组变量所指的数组所做的修改将会影响原有的数组的元素。
注意:
(1)、对已一维数组,arr[i]与*(arr + i)等价;
(2)、对于二维数组,arr[i] + 2与*(arr + i) + 2等价;
对于二维数组float arr[3][4];有如下定义
char* str = "I Love iOS";
NSLog(@"%s",str);//I Love iOS
str += 7;
NSLog(@"%s",str);//iOS
void copyString(char* to,char* from){
while (*from) {
*to++ = *from++;
}
*to = '\0';
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
char* str = "www.cratit.org";
char dest[100];
copyString(dest,str);
NSLog(@"%s",dest);//www.cratit.org
copyString(dest,"Objective-C isFuncy!");
NSLog(@"%s",dest);//Objective-C isFuncy!
}
return 0;
}
注意:
(1)、字符数组底层真正存放了所有的字符,每个字符对应一个数组元素;而字符指针指向字符数组时只存放字符数组的首地址。
(2)、字符数组只能定义时赋值。
例如:
正确地:
char str[100] = "www.fkjava.org";
错误的:
char str[100];
str = "www.fkjava.org";
另外:字符指针完全可以重复赋值
char* str = "www.fkjava.org”;
也支持
char* str;
str = "www.fkjava.org"
(3)、定义字符数组时,程序会为每个数组分配内存空间,但定义字符指针变量时,程序只是定义一个指针变量,该指针所指向的内存单元是不确定的。
使用函数指针的步骤如下:
(1)、定义函数指针变量。
格式如下:
函数返回值类型(* 指针变量名)();
(2)、将任何已有的函数入口赋值给函数指针变量
例如:fnpt = avg;
注意:
1)C语言允许将任何已有的函数赋值给函数指针变量,因此,同一个函数指针变量在不同的时间可指向不同的函数;
2)为函数指针变量赋值时,只要给出函数名即可,无须在函数后使用括号,也无须传入参数。注意是讲函数入口赋给指针变量,而不是调用函数后将返回的结果赋给函数指针变量。
(3)、使用函数指针变量来调用函数。
使用函数指针变量调用函数的语法格式为:
(*函数指针变量)(参数);
注意:必须先用()把*函数指针变量括起来,用于保证获取该指针变量所指的函数,然后执行函数调用。
//最大值
int max(int* data,int len){
int max = *data;
for (int* p = data; p < data + len; p++) {
if (*p > max) {
max = *p;
}
}
return max;
}
//平均值
int avg(int* data,int len){
int sum = 0;
for (int* p = data; p < data + len; p ++) {
sum += *p;
}
return sum/len;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int data[] = {20,12,8,36,24};
//定义函数指针变量
int(*fnpt)() = max;
NSLog(@"最大值:%d",(*fnpt)(data,5));
fnpt = avg;
NSLog(@"最大值:%d",(*fnpt)(data,5));
}
return 0;
}
void map(int* data,int len,int(*fn)()){
//采用指针遍历data数组的元素
for (int* p = data; p < data + len; p ++) {
//调用fn函数(fn函数是动态传入的)
printf("%d\n",(*fn)(*p));
}
}
int noChanage(int val){
return val;
}
//定义一个计算平方的函数
int square(int val){
return val*val;
}
//定义一个计算m³的函数
int cube(int val){
return val*val*val;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int data[] = {20,12,8,36,24};
//下面程序代码3次调用map()函数,每次调用时传入不同的函数
map(data,5,noChanage);
NSLog(@"计算数组元素的平方");
map(data,5,square);
NSLog(@"计算数组元素的立方");
map(data,5,cube);
}
return 0;
}
为了保证函数返回的指针是有效的,有两种方式:
优先级:()>[]>*
数组指针是指向数组首元素的地址的指针,其本质为指针。
指针数组是数组元素为指针的数组,其本质为数组。
声明指针数组语法:
类型* 数组变量[长度];由于[]运算符的优先级比*优先级高,因此,数组变量先与后面的[]结合成数组形式,前面的“类型*”则用于指定多个数组元素类型,且声明每个数组元素都是指针。
切记:不要写成这个样子
类型 (变量名)[长度];(变量名)先形成一个整体,代表一个指针变量,该指针变量指向一维数组,因此表示定义一个指向一维数组的指针变量。
void sort(char* names[],int n){
char* tmp;
for (int i = 0; i < n - 1; i ++) {
//用第i个字符串 依次与后面的每个字符串相比
for (int j = i + 1; j < n; j ++) {
//如果names[i]的字符串大于names[j]的字符串,交换他们
//就可以保证第i个位置的字符串总比后面的所有字符串小
if (strcmp(names[i], names[j])) {
tmp = names[i];
names[i] = names[j];
names[j] = tmp;
}
}
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int nums = 5;
char* strs[] = {"Objective-C","iOS","Java","Ajax","Android"};
sort(strs,nums);//对字符串排序
for (int i = 0; i < nums; i ++) {
NSLog(@"%s",strs[i]);
}
}
return 0;
}
声明指向指针变量的指针语法格式:
类型** 变量名;
定义结构体:struct是个关键字,成员列表中可以定义任意多个成员变量这些成员变量即可是基本类型,也可以是结构体类型。
struct 结构体类型名
{
//成员列表
};
例如:
struct point
{
int x;
int y;
};
(1)、先定义结构体类型,在定义结构体变量
格式如下:
struct 结构体名 变量名;
例如:
struct point p1;
注意:每次定义变量时都需要使用struct关键字,比较繁琐,现有两种方法解决:
第一种:使用#define预编译指令
#define POINT struct point
POINT p1;
第二种:使用typedef为已有的结构体类型定义新名称。
(2)、同时定义结构体类型和结构体变量
语法格式如下:
struct 结构体名
{
//成员列表;
}结构体变量1,结构体变量2...;
例如:
struct point{
int x;
int y;
}p1,p2;
语法格式:
typedef 已有类型 新名称;
typedef int Counter;
Counter i,j;
定义结构体
struct CGSize {
CGFloat width;
CGFloat height;
};
typedef struct CGSize CGSize;
struct point{
int x;
int y;
}p1 = {12,21};
struct point p2 = {12,321};
p2.x = 13;
p2.y = 31;
struct point points[] = { {12,321},
{321,443},
{1,3},
};
points[1].x = 123;
points[1].y = 123;
如下
enum DAY
{
MON=1,
TUE,
WED,
THU,
FRI,
SAT,
SUN
};
定义快的语法格式
^[块返回值类型] (形参类型1 形参1,…)
{
//块执行体
}
定义块与定义函数的区别:
注意:如果多次调用已经定义的块,那么程序应该将该块赋给一个块变量,定义块变量的语法格式如下:
块返回值类型(^块变量名)(形参类型1,…);
例如:
int (^printStr)(int , int ) = ^int(int a,int b)
{
NSLog(@"我正在学习Objective_C的块");
return a + b;
};
NSLog(@"sum = %d",printStr(3,4));//sum = 7
块可以访问程序中局部变量的值,当块访问局部变量的值时,不允许修改局部变量的值;
例如
int num = 20;
void (^printStr)() = ^void(void)
{
//num = 30;不能对num修改
NSLog(@"num = %d",num);//num = 20;
};
num = 45;
printStr();
输出20的原因:当程序使用块访问局部变量时,系统在定义块时就会把局部变量的值保存在块中,而不是等到执行时采取访问、获取局部变量的值。
修改如下:
__block int num = 20;
void (^printStr)() = ^void(void)
{
NSLog(@"num = %d",num);//num = 45;
num = 30;//允许修改局部变量
NSLog(@"num = %d",num);//num = 30;
};
num = 45;
printStr();
NSLog(@"num = %d",num);//num = 30;
用途:
(1)、复用块变量类型,使用块变量类型可以重复定义多个块变量;
(2)、使用块变量类型定义函数参数这样即可定义带块参数的函数。
语法格式如下:
typedef 块返回值类型(^块变量类型)(形参类型1,...);
例如:
typedef void(^FinishBlock)(NSData* data);
typedef void(^FailedBlock)();
@property (nonatomic, copy) FinishBlock finishBlock;
@property (nonatomic, copy) FailedBlock failedBlock;
self.finishBlock(_mData);
self.failedBlock();
再例如:
typedef void (^FKPrintBlock) (NSString *);
typedef void (^FKPrintBlock) (NSString *);
FKPrintBlock print = ^(NSString *info)
{
NSLog(@"info = %@",info);//info = C;
};
print(@"C");