您的位置:首页 > 其它

进阶指针与数组——函数指针,函数指针数组等

2018-04-30 17:49 417 查看

下面与大家一起讨论一下进阶版的指针与数组,是建立在大家的一级指针和一维数组的基础上的。

  • 指针数组
  • 数组指针
  • 函数指针
  • 函数指针数组
  • 指向函数指针数组的指针

1.指针数组

存放指针的数组。
因为 [ ] 操作符的优先性要高于 * 操作符,所以下面的均为指针数组:

int *arr1[10]; 表示指针数组,强调的是数组。数组有十个元素,是指向int类型数据的指针。

char *arr2[4]; 表示指针数组,强调的是数组。数组有4个元素,是指向char类型数据的指针。

2.数组指针

指向数组的指针,(由于数组可以被取地址)
因为()操作符的结合性高于 * 操作符,所以以下为指针的定义。例:
(1)
int arr[10]={0};
int(* p)[10]=&arr;
意思是* p先是指针,再指向数组,这个数组里面有10个元素,每个元素是int整型。
(2)
int (*p2)[10];
是一个指 针,它指向一个包含 10 个 int 类型数据的数组,即数组指针.

(拓展)多维数组和多级指针

这里是一维数组和一级指针的进阶版本,在我们搞明白了这里以后,以后的按照概念推出来即可。
(1)二维数组:我们可以将其想象成一个矩阵,如下图:

但我们也需要明白实质:二维数组并不是表格样的,而是线性的。按照首地址开始,然后依次排列在内存中。如:

我们可以用a[ i ][ j ]来访问该处的数据,也可以用指针来访问,((a+i)+j)。
(2)二级指针:char **p;
二级指针归根到底还是一个指针,它就是保存一级指针地址的指针。
那么我们应该怎么使用p呢?下面就是使用步骤:
第一步:根据 p 这个变量,取出它里面存的地址。
第二步:找到这个地址所在的内存。
第三步:用钥匙打开这块内存,取出它里面的地址,*p 的值。
第四步:找到第二次取出的这个地址。
第五步:用钥匙打开这块内存,取出它里面的内容,这就是我们真正的数据,**p 的值。

3.数组传参和指针传参

我们在应用数组和指针时,传参到函数是不可避免的一步,那么怎么才算是正确的传参呢?
在C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元 素首地址的指针。
(1)例如:(一维数组)

int arr[10]={0};
int *arr2[20]={0};
tsst(arr);
test2(arr2);

则,传参的种类可以是

void test(int   arr[])
void test(int   arr[10])
void test(int   *arr)

void test2(int  *arr2[20])
void test2(int  **arr2)

(2)例如:(二维数组)

int arr[3][5]={0};
test(arr);

则,传参的种类可以是

void test(int   arr[3][5])
void test(int   arr[][5])

为什么第二维的维数却不可省略:这是因为我们可以忽略第一维的维数,通过地址访问数据。但是我们必须知道在哪个位置数据到达了第几行。
void test(int (*arr)[5])

(3)例如:(一级指针)
一级指针用一级指针接收。

(4)例如:(二级指针)
若实参部分是二级指针**arr,则可以用于接收的形参为:二级指针、一级指针的地址、指针数组

4.函数指针

这个词的完整表述应该是指向函数的指针,那么我们就可以知道,这个概念的实质是一个指针。
如:
A. char * fun1(char * p1,char * p2);
B .char * fun2(char p1,char * p2);
C. char * (fun3)(char p1,char * p2);

A 中,fun1 是函数名,p1,p2 是参数,其类型为 char 型,函数的返回值为 char 类型。
B中,与A类似,不过该函数的返回值为char **类型。
C中,这里fun3先和()操作符结合,所以fun3为指针变量,指向一个函数。这个函数有两个指针类型的参数,函数的返回值也是一个指针。其实也就是
char * ()(char p1,char * p2) fun3;

1.void test()
{
printf("hehe\n");
}
int main()
{
//printf("%p\n", &test);
//printf("%p\n", test);
void (*p)()=test;   //先声明和定义,把test的地址赋值给 p 。void (*)()表示类型
(*p)(); //(*p)表示先找到这个函数,然后再()函数调用,间接访问传递参数。
system("pause");
return 0;
}

下面来个小栗子,希望大家不要被吓到:
(1)

(*(void(*)())0)();

不要着急,我们可以一步步分析:
第一步:
void(*) ()
,可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。 第二步:
(void(*) ())0
,这是将 0 强制转换为函数指针类型,0 是一个地址,也就是说一
个函数存在首地址为 0 的一段区域内。
第三步:
(*(void(*) ())0)
,这是取 0 地址开始的一段内存里面的内容,其内容就是保存 在首地址为 0 的一段区域内的函数。
第四步:
(*(void(*) ())0)()
,这是函数调用。
(2)
void (*signal(int,void(*)(int)))(int)

同样,可以先将其转化一下,表示的是
void (*)(int) signal(int,void(*)(int))
,返回类型是函数指针void(*)。参数为(int)。则:
(1)signal是一个函数
(2)signa函数的参数有两个,第一个为int类型,第二个为函数指针类型,该指针指向的函数有一个整型参数,返回类型为void。
(3)signal函数的返回类型为一个函数指针类型,该指针指向的函数有一个整型参数,返回类型为void。

5.函数指针数组

这里我们研究一下函数指针数组,顾名思义,就是存放函数指针的数组。(因为数组可以存放各种数据类型,除了函数)定义如下:

char *  (*pf[3])(char *  p);

我们可以分析一下这个表达式的组成:它是一个数组,数组名为 pf,数组内存储了 3 个指向函数的 指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函 数。

6.函数指针数组的指针

看到这个,大家的小心脏是不是不争气的停了一下?不要着急,经历了那么多洗礼,我们应该要有免疫,也应该懂得一点套路:先看被修饰的成分,“指针”。哦,无非就是个指针嘛!不过这个指针这个指针指向一个数组,这个数组里面存的都是指向函数的指针。也就这样吧。
定义如下:

char *(*(*pfun)[5])(char *p);

函数指针数组和函数指针数组的指针这两个应用详见下一篇博客。谢谢驻足,各位!

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐