进阶指针与数组——函数指针,函数指针数组等
下面与大家一起讨论一下进阶版的指针与数组,是建立在大家的一级指针和一维数组的基础上的。
- 指针数组
- 数组指针
- 函数指针
- 函数指针数组
- 指向函数指针数组的指针
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);
函数指针数组和函数指针数组的指针这两个应用详见下一篇博客。谢谢驻足,各位!
阅读更多- 函数指针和函数指针数组
- 指针数组、数组指针、指针函数、函数指针、函数指针数组、对象指针解析
- 数组指针、 指针数组、函数指针、函数指针数组和指向函数指针数组的指针
- 个人理解:关于指针数组、数组指针、函数指针、函数指针数组、函数指针数组指针
- 数组指针、指针数组、函数指针、函数指针数组、函数指针的数组的指针
- 关于函数指针数组与返回数组指针的函数
- 函数指针与函数指针数组的定义与使用
- 函数指针数组与返回数组指针的函数
- 对指针数组 ; 数组指针 ; 函数指针 ; 函数指针数组 ; 指向函数指针数组的指针的理解
- 对于数组指针—指针数组;函数指针—函数指针数组,指向函数指针数组的指针,的理解
- 函数指针与函数指针数组的使用方法
- 函数指针与函数指针数组的使用方法
- 函数指针—指针函数 数组指针—指针数组 类模板—模板类 函数模板——模板函数 的区别
- 数组指针,指针数组,函数指针,函数指针数组,函数指针数组指针
- 理解数组指针、指针数组、函数指针、函数指针数组
- c--函数指针,函数指针数组,回调函数
- 指针数组,数组指针,存放数组指针的数组,指向存放数组指针数组的指针,函数指针,函数指针数组,指向函数指针数组的指针
- 关于函数指针及函数指针数组
- C++ primer(七)--函数、C++的编程模块 函数指针 函数指针数组 typedef
- 理解指针数组、数组指针、函数指针、函数指针数组、指向函数指针数组的指针