(一)、二维数组与二级指针参数数组
二维数组作参数:ide
二维数组作参数与一维数组作参数同样,传递的都是首元素的地址,只不过二维数组的每一个元素又是一个一维数组 。函数
例:int arr[5][10];url
这是一个5行10列的×××数组,能够将它当作一个只有5个元素的一维数组,只不过每一个元素又是一个大小为10的一维数组。spa
fun(arr);咱们来分析一下当arr做为实参是,须要什么类型的形参来接受它???指针
arr做为参数时,表明首元素地址,要接受这个地址必须是一个指针或者是一个数组,可是他的每一个元素的类型是int[10]。get
因此咱们能够用一个指向类型为int [10]的数组指针来接受arr[5][10],即:int (*p)[10] 。 一样也能够用一个数组来接受arr,即:int arr1[][10],这里第二维的大小不能省略,在这里int arr1[][10] 也能够被解析成int (*arr1)[10];input
二级指针作参数:回调函数
例:char *p[5];it
这是一个大小为5,每一个元素都是char *类型的数组,当他做为实参时,须要什么样类型的形参来接受他呢???
fun(p); 分析:既然p是一个数组,那么数组在做为实参时,实际上传递过去的是首元素的地址,可是p的每个元素都是一个char *类型,也是一个地址,因此形参的类型应该是char **。
总结:
数组名只有在sizeof()和取&时才不发生降级,其他地方都表明首元素地址。
(二)、函数指针
函数指针: 是一个指向函数的指针
声明:
例:void (*p)(); 首先p是一个指针,它指向一个返回值为空,参数为空的函数。
初始化:
要明确,函数指针本质仍是一个指针,既然是指针,那么就必须给他进行初始化才能使用它,下面来看一看函数指针的初始化,定义一个返回值为空参数为空的函数:void fun()。
p=fun;
p=&fun;
这两种初始化的方式都是正确的。由于函数名在被编译后其实就是一个地址,因此这两种方式本质上没有什么区别。
调用:
p();
(*p)();
这两种方式都是正确的。p里面存的fun的地址,而fun与&fun又是同样的。
例:分析一下(*(void (*) () ) 0 ) ()是个什么东东!!!
首先,void (*) ();是一个返回值为空,参数为空的函数指针类型。
void (*) () 0 ; 它的做用是把0强制类型转换成一个返回值为空,参数为空的函数指针。
(*(void (*) () )0) ; 找到保存在0地址处的函数。
(*(void (*) () ) 0 ) (); 对这个函数进行调用。
用途:
一、回调函数:用户将一个函数指针做为参数传递给其余函数,后者再“回调”用户的函数,这种方式称为“回调函数”,若是想要你编写的函数在不一样的时候执行不一样的工做,这时就可使用回调函数。回调函数也算是c语言里面为数很少的一个高大上的东西。
二、转换表:转换表本质上是一个函数指针数组,说白了就是一个数组,只不过数组里面存放的所有都是函数指针。
例:实现一个计算器
#include<stdio.h> #include<stdlib.h> #include<assert.h> int Add(int a, int b) { return a + b; } int Sub(int a, int b) { return a - b; } int Mul(int a, int b) { return a *b; } int Div(int a, int b) { assert(b != 0); return a / b; } int operator(int (*fun)(int,int)) //回调函数 { int a = 0; int b = 0; printf( "请输入操做数:" ); scanf( "%d%d", &a, &b); return (*fun )(a,b); } int main() { printf( "*************************************\n" ); printf( "*0.exit 1.Add****\n" ); printf( "*2.Sub 3.Mul****\n" ); printf( "*4.Div *********\n" ); int(*fun[5])(int ,int); //转换表 fun[1] = Add; fun[2] = Sub; fun[3] = Mul; fun[4] = Div; int input = 1; while (input) { printf( "请选择> " ); scanf( "%d", &input); if (input<0 || input>4) { printf( "选择无效\n" ); } else if (input == 0) { break; } else { int ret = operator(fun[input]); printf( "%d\n", ret); } } system( "pause"); return 0; }
在这个计算器程序中,就使用到了转换表fun[]。fun[]里面存放了加减乘除四个函数,根据input的不一样,fun[input]调用不一样的函数。这种方式与switch() case的功能比较类似,不过转换表比switch()case更简单。
函数指针数组:
函数指针数组是一个指针数组,也就是一个数组,只不过里面存放的全都是函数指针。
声明:例: char* (*p[5])(int,int);
p与[5]先结合成一个数组,因此这是一个大小为5的数组,数组元素的类型是一个函数指针,这个函数指针指向一个返回值为char *,有两个参数,且参数类型为int,int的数组。
能够这样理解:char * (*)(int,int) p[5]; 其中char*(*)(int int)是p[5]的类型。
函数指针数组的指针:
函数指针数组的指针是一个数组指针,本质上是一个指针,只不过指向的是一个存放函数指针的数组。
声明:例:char *(*(*p)[5])(int,int);
*与p先结合成一个指针,因此p是一个指针,指针所指向的是一个大小为5的数组,这个数组存放的是函数指针,这些函数指针所指向的类型是返回值为char *,有两个int型参数的函数。
能够这样理解: char *(*[5])(int,int) *p; p是一个指针,类型是char*(*[5])(int,int).
总结:指针数组,是一个数组,里面存放的是指针。
数组指针,是一个指针,指向一个数组的指针。
函数指针数组,是一个数组,里面存放的是函数指针。
函数指针数组指针,是一个指针,指向一个存放函数指针的数组。
其实规律很简单,强调的都是最后两个字,最后两个字是什么他就是什么,而前面的字就是他的类型。