您的位置:首页 > 其它

新手,对函数,函数指针,回调函数, 函数指针作为函数的返回值和block的一些见解

2015-02-27 17:15 323 查看
很多初学者,学c语言时,看到函数都觉得难,我也是,一开始觉得函数太难了,后来慢慢就理解了其实函数,还有OC的selector都是为了把具有某种功能的一段代码单独提取,以便重复运用(直接调用就行,不用重复写)
第一:函数在c语言中最简单的函数一般有四种:1, 无返回值, 无参数.2, 无返回值,有参数.3, 有返回值,无参数.4, 有返回值,有参数1, 无返无参 定义方式: void 函数名();一般不怎么用这种形式的函数,可用来输出,打印void functionOne() { printf("无返回值,无参数"); // 没有返回值不能写return}int main(int argc, const char * argv[]) { // 没有参数也得写括号() functionOne(); return 0;}也可新建头文件(.h)和实现文件(.m)来声明函数和实现函数2, 无返有参 定义方式: void 函数名(int, a);void functionTwo(int a) { printf("无返回值,有参数 %d", a); // 没有返回值不能写return}int main(int argc, const char * argv[]) { functionTwo(10); return 0;}3, 有返无参 定义方式: (返回值类型int) int 函数名();int functionThree() { // 有返回值必须有return, 而且在调用函数时需要用一个跟函数的返回值同类型的容器(变量)接受该返回值或者直接输出(打印) return 5;}int main(int argc, const char * argv[]) { // 没有参数也得写括号() printf("%d", functionThree()); return 0;}4, 有返有参 定义方式: (返回值类型)int functionFour(int a, int b) { // 返回两个数之间的最大值 return a < b ? b : a;}int main(int argc, const char * argv[]) { // 参数得一一对应, 注意顺序和参数类型 printf("%d", functionFour(3, 5)); return 0;}5, 有时候我们只是需要对数据进行修改,不需要修改值然后返回再接受来用,而是直接在函数体内直接改值,此时我们不能直接传值,而是得传地址,只有修改地址内所包含的内容的值才能真正的修改值(其实就是局部变量和全局变量的问题),传地址的话代码如下:void functionChangeValue(int *a) { ++*a;}
int main(int argc, const char * argv[]) { int a = 5;
functionChangeValue(&a) printf("%d", a); return 0;}第二: 函数指针我们都知道指针是什么,指针是用来存放地址的(也可以理解为指向某一块内存空间, 可以对该内存空间里的内容进行操作)函数里传送的参数是个形参,从主调函数(也可以是别的函数,如果是别的函数调用的话,也叫函数回调, 说白了就是在主函数以外的函数里调用另一个函数)传送到被调函数之后,外界无法访问被调函数里定义的参数,这时我们要是有指针就可以解决局部变量带来的局限性,可以让指针变量当形参,这时我们在被调函数里操作的时该指针指向的内存空间里的内容,而不是被调函数里另定义的变量,这样我们可以大大的提高程序的开发其实对于函数指针来讲,指针的作用不是特别大,因为函数的名字本身就代表着该函数的地址一, 函数指针int functionFour(int a, int b) { return a < b ? b : a;}int main(int argc, const char * argv[]) { // 函数类型跟函数指针的类型必须完全匹配 // 代表函数的类型的是 : int (int, int) // 函数返回值类型,形参类型,一个不能少,少一个就不是该类型的函数 // *p 外面的括号不能省 int (*p)(int, int) = NULL; // 函数名代表函数的地址,地址赋给函数指针变量p之后可以调用 p = functionFour; // 调用,形式跟直接调用函数没什么区别,只是名字变了 printf("%d", p(3, 5)); return 0;}其实函数指针,只这么用的话函数指针的用处不明显
二, 回调函数int functionFour(int a, int b) { return a < b ? b : a;}int functionFive(int a, int b, int c, int (*p)(int, int)) { // 回调函数 // 把functionFour当参数传到functionFive里调用,实现返回三个参数中的最大值
// 如果我们没有用函数指针做参数,我们就得在函数里直接调用该函数,要是当参数传的目的还是那句,提高代码的重用性 // 把函数的地址赋给p // p = functionFour
// p(b, c)返回b和c中的最大那个,然后返回过来的最大值跟a比较 // 再返回a和p(b, c)中的最大值 return a < p(b, c) ? p(b, c) : a;}int main(int argc, const char * argv[]) { // 先定义一个函数指针,用来存放 int (int a, int b, int c, int (*p)(int, int)) 类型的函数的地址 int (*p1)(int, int, int,int (*p)(int, int)) = NULL; // 再定义一个函数指针,存放int (int a, int b)类型的函数的地址 // functionFive的地址赋给p1 p1 = functionFive // 调用的格式如下 printf("%d", p1(3, 4, 5, p2)); return 0;}回调函数中,函数指针的作用还是有所体现的,最能体现函数指针作用的应该是接下来跟大家介绍的函数指针作为函数的返回值
第三, 函数指针作为函数的返回值// 将int (int, int)类型的函数改名为PFUNtypedef int (*PFUN)(int a, int b);// 定义一个结构体Function改名为function// 有两个成员变量// 一个是函数的名字,一个是对应的函数(地址), 可通过函数名字查找该函数typedef struct Function { char funName[20]; PFUN fun;}function;// 定义几个同类型的函数,存放在结构体中int functionFour(int a, int b) { return a < b ? b : a;}int functionSix(int a, int b) { return a + b;}int functionSeven(int a, int b) { return a * b;}// 函数返回值为PFUN类型(也就是说 int (int, int) 类型)// 从数组p(存放的是上面三个函数)中找名字为funName的函数PFUN functionEight(char funName[], function p[], int funCount) { // 定义一个指针变量,类型是PFUN类型,NULL是初始化 PFUN fun = NULL; // 便利结构体数组(里头存放的成员是函数) for (int i = 0; i < funCount; i++) { // 字符串比较,如果函数名跟传来的函数名一样,就把函数地址放在fun里头返回给主调函数 if (strcmp((p + i)->funName, funName) == 0) { fun = (p + i)->fun; // 如果一开始就找到了就不需要继续循环,在此我们得用break; break; } } // 返回找到的函数 return fun;}int main(int argc, const char * argv[]) { // 先把函数存放在指针里(可以直接赋给function1, 2, 3) PFUN fun1 = functionFour; PFUN fun2 = functionSix; PFUN fun3 = functionSeven; // 给结构体成员赋值,函数名和函数地址 function function1 = {"maxValue", fun1}; function function2 = {"sumValue", fun2}; function function3 = {"multiply", fun3}; // 把结构体成员存放在结构体数组内 function fun[3] = {function1, function2, function3}; // 调用函数,把搜索到的函数返回给Function // 再调用该函数,实现我们想要的功能 PFUN Function = functionEight("multiply", fun, 3); // 可输出 12 printf("%d", Function(3, 4)); return 0;}函数指针作为函数的返回值时,能体现函数指针的用处,比如排序问题上,Boss第一天要你用名字排序,第二天用年龄排序,这时我们得修改我们的代码,有时候有可能大量的去修改我们的代码,这时候我们要是用函数指针作为函数指针,我们可以只修改搜索函数名字时的 funName 就行,就可以实现我们想要的功能,能大大的提高代码的重用性其实函数指针作为函数返回值时,我们一般都是跟结构体一起用,其实就是把类型相同的函数当做结构体的成员,该成员有函数的地址(函数名),还有一个是能找到函数的一个标记,就是用来找该函数的名字(在这里我们一般用一个字符串查找,能见名知意,提高代码的可读性)
第四, block语法第一, block说到这儿,block语法跟函数很相似,也有四种1, 无返无参
// myBlock 是 block 块语法的名// block 的类型是:void (^)(void)// 等号右边是 block 的语法结构// ^(void)是block得参数列表, 如果没有参数, (void)可以不写,但 ^ 必须写// { }里是函数体, 大括号外要加分号结束 void (^myBlock)(void) = ^(void) { NSLog(@"无参数无返回值!"); }; void (^aBlock)(void) = ^(void) { NSLog(@"%@", @"5"); }; void (^bBlock)(void) = ^(void) { NSLog(@"%@", @"string"); }; void (^cBlock)(void) = ^(void) { NSLog(@"%@", @"string"); };
// 调用,必须加括号,跟调用函数一样 myBlock();2,有返无参 NSInteger (^myBlock)(void) = ^(void) { return (NSInteger)5; };
// 得用一个类型跟block的返回值相同的容器(变量)来接受 NSInteger result = myBlock(); NSLog(@"%ld", result);3, 无返有参
void (^myBlock)(NSInteger) = ^(NSInteger num) { NSLog(@"%ld", num); }; myBlock((NSInteger)5);4, 有参有返
NSInteger (^myBlock)(NSInteger, NSInteger) = ^(NSInteger num1, NSInteger num2) { return num1 + num2; }; NSLog(@"%ld", myBlock((NSInteger)5, (NSInteger)10));5,最终要的一点就是,block真正用法不是以上4种形式可以打断点看看,先走到block赋值,就是block等号走完,断点直接跳过block语法块内部实现,而block内部是在调用block时才走,所以block的真正作用跟协议有点类似,block内部的语句,我们想让它什么时候执行,我们就在什么时候调用block(比如,按钮的点击事件或者网络请求结束后传值时),但是一般推荐使用协议,协议比block省内存,因为block把代码块从栈内存copy到堆内存(ARC下自动copy,如果是属性得用copy修饰,如果是MRC的话,我们得手动copy,否则容易crash),然后执行,所以会占内存,相比之下协议比较好,虽然步骤繁琐点
第二, block做参数不知道学OC的时候有没有人跟我一样想过,block能不能也当做一个参数传到另一个block?答案是完全可以的. 但是我们一般不这样用,OC中我们都是直接在方法里调用另一个方法来实现,但我在这儿满足一下有好奇心的朋友(其实这样用的话感觉不如直接在block中用,会麻烦些)(这个方法知道用法就行, 一般也用不着, 仅供参考, 勿喷)
// 直接用刚才的block了 void (^myBlock)(NSInteger) = ^(NSInteger num){ NSLog(@"%ld", num); }; // 定义一个block // 第一个参数NSString类型 第二个是一个block(不要忘了block的特征符号^)(参数NSInteger) // 第三个参数NSInteger // 注意等号右边参数, void (^p)(NSInteger) 符号^代表p是一个block类型的参数 void (^aBlock)(NSString *, void (^)(NSInteger), NSInteger) = ^(NSString *string, void (^p)(NSInteger),NSInteger num1){ NSLog(@"%@", string); // p没有返回值,所以直接用来打印 p(num1); }; // myblock地址给^p1 void (^p1)(NSInteger) = myBlock; // 调用aBlock 直接把p1当参数传 aBlock(@"string", p1,10);
第三, block做返回值其实,这也是我一个很无聊的想法,完全就是参照函数的,block也是可以做返回值的,可以做函数的返回值,我们一般没有这种需求,但是突然有一天你需要实现某种效果(比如是在做很强大的算法(用openGL让图片曲面变换,需要很多变态的算法),在别人的demo里看到过),这事你有可能需要,真有这需要的时候说明你已经很厉害了
以上内容仅供跟我一样的新手一个参考,或者交流,勿喷,谢谢~~~~~~

本文出自 “封寒瑾” 博客,转载请与作者联系!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: