(一)、二维数组与二级指针参数


   二维数组做参数:

        二维数组做参数与一维数组做参数一样,传递的都是首元素的地址,只不过二维数组的每个元素又是一个一维数组 。

例:int arr[5][10];

       这是一个5行10列的整形数组,可以将它看成一个只有5个元素的一维数组,只不过每个元素又是一个大小为10的一维数组


fun(arr);我们来分析一下当arr作为实参是,需要什么类型的形参来接受它???

       arr作为参数时,代表首元素地址,要接受这个地址必须是一个指针或者是一个数组,但是他的每个元素的类型是int[10]。


       所以我们可以用一个指向类型为int [10]的数组指针来接受arr[5][10],即:int (*p)[10] 。    同样也可以用一个数组来接受arr,即:int arr1[][10],这里第二维的大小不能省略,在这里int arr1[][10] 也可以被解析成int (*arr1)[10];


二级指针做参数:

例:char *p[5];

        这是一个大小为5,每个元素都是char *类型的数组,当他作为实参时,需要什么样类型的形参来接受他呢???


fun(p);       分析:既然p是一个数组,那么数组在作为实参时,实际上传递过去的是首元素的地址,但是p的每一个元素都是一个char *类型,也是一个地址,所以形参的类型应该是char **。


总结:

spacer.gifwKiom1clqGqSbiKWAABFE00QjMY890.png

数组名只有在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 ) (); 对这个函数进行调用。


用途:

       1、回调函数:用户将一个函数指针作为参数传递给其他函数,后者再“回调”用户的函数,这种方式称为“回调函数”,如果想要你编写的函数在不同的时候执行不同的工作,这时就可以使用回调函数。回调函数也算是c语言里面为数不多的一个高大上的东西。

        2、转换表:转换表本质上是一个函数指针数组,说白了就是一个数组,只不过数组里面存放的全部都是函数指针。

例:实现一个计算器

#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).


总结:指针数组,是一个数组,里面存放的是指针。

数组指针,是一个指针,指向一个数组的指针。

函数指针数组,是一个数组,里面存放的是函数指针。

函数指针数组指针,是一个指针,指向一个存放函数指针的数组。

其实规律很简单,强调的都是最后两个字,最后两个字是什么他就是什么,而前面的字就是他的类型。