文章目录
- 一、指针数组与数组指针
- 1.1 指针数组与数组指针的表达式
- 二、函数指针
- 2.1 函数指针的书写方式
- 三、二重指针与一重指针
- 3.1 二重指针的本质
- 3.2 二重指针的用法
- 3.3 二重指针与数组指针
- 总结
一、指针数组与数组指针
(1)指针数组的实质是一个数组,这个数组中存储的内容全部是指针变量。
(2)数组指针的实质是一个指针,这个指针指向的是一个数组。
1.1 指针数组与数组指针的表达式
int *p[5]; int (*p)[5]; int *(p[5]);
一般规律:int *p;(p是一个指针); int p[5];(p是一个数组)
第一个:int *p[5];
核心是p,由于[]的优先级高于*,因此p先跟[]结合,p是一个数组,数组中的元素都是指针类型,指针指向的元素类型是int类型的;因此整个符号是一个指针数组。
第二个,int (*p)[5];
当存在()时候,作用是强制将()内的元素进行强制结合,无视优先级。因此先看()内的运算,()内核心是p,离他最近的是*,因此p是一个指针。*p再跟[]结合,说明指针p指向一个数组,数组有5个元素,数组中存的元素都是int类型;总结一下整个符号的意义就是数组指针。
第三个,int *(p[5]);
解析方法和结论和第一个相同,()在这里是可有可无的,因为它可以无视优先级,强制将p和[]进行结合,因此p是数组。
注意:符号的优先级到底有什么用?其实是决定当2个符号一起作用的时候决定哪个符号先运算,哪个符号后运算。
当存在()时候,作用是强制将()内的元素进行强制结合,无视优先级。
遇到优先级问题怎么办?
第一,查优先级表;
第二,自己记住(只要记住[] . ->这几个优先级比较即可)。
二、函数指针
函数指针的实质(还是指针变量)
(1)函数指针的实质还是指针,还是指针变量。本身占4字节(在32位系统中,所有的指针都是4字节)
(2)函数指针、数组指针、普通指针之间并没有本质区别,区别在于指针指向的东西是个什么玩意。
(3)函数的实质是一段代码,这一段代码在内存中是连续分布的(一个函数的大括号括起来的所有语句将来编译出来生成的可执行程序是连续的),所以对于函数来说很关键的就是函数中的第一句代码的地址,这个地址就是所谓的函数地址,在C语言中用函数名这个符号来表示。
(4)结合函数的实质,函数指针其实就是一个普通变量,这个普通变量的类型是函数指针变量类型,它的值就是某个函数的地址(也就是它的函数名这个符号在编译器中对应的值)
2.1 函数指针的书写方式
(1)C语言本身是强类型语言(强类型语言的定义:每一个变量都有自己的变量类型),编译器担心我们定义的类型不匹配,因此要帮我们做严格的类型检查。这里要注意区分python这类的脚本语言、makefile,属于弱类型语言,对变量是没有类型定义的。
(2)所有的指针变量类型其实本质都是一样的,但是为什么在C语言中要去区分它们,写法不一样呢(譬如int类型指针就写作int *p; 数组指针就写作
int (*p)[5],函数指针就得写得更复杂)
int *p;
int a[5];
p=a;类型匹配,所以编译器不会警告也不会报错
p=&a;类型不匹配,p是int *类型,而&a是int (*)[5]类型。
int (*p1)[5];
p1=&a;类型匹配,都是int (*)[5]类型
假设有个函数是:void func(void);
对应的函数指针:void (*p)(void);
类型是:void (*)(void);通过指针来调用函数。
函数名称作为右值表示一坨代码的首地址,因此pFunc和func1(函数名)的类型是匹配的,都是void *(void)。pFunc就指向了函数func1。
(4)函数名和数组名最大的区别就是:函数名做右值时加不加&效果和意义都是一样的;但是数组名做右值时加不加&意义就不一样。这是编译器自己定义的。
(5)写一个复杂的函数指针的实例:譬如函数是strcpy函数(char *strcpy(char *dest, const char *src);),对应的函数指针是:char *(*pFunc)(char *dest, const char *src);
strcpy就是将复制功能,结果是:a=abc
三、二重指针与一重指针
(1)二重指针和一重指针本质都是指针变量,指针变量的本质就是变量。
(2)一重指针变量和二重指针变量本身都占4字节内存空间(32位平台)。
3.1 二重指针的本质
(1)二重指针和普通指针的差别就是它指向的变量类型必须是个一重指针。二重指针其实也是一种数据类型,编译器在编译时会根据二重指针的数据类型来做静态类型检查,一旦发现运算时数据类型不匹配编译器就会报错。
Char**类型就是指针指向的变量是char *类型。
Char *类型指针指向的变量是char类型。
Char **p:二重指针
char *p:一重指针
(2)C语言中如果没有二重指针行不行?其实是可以的。一重指针完全可以做二重指针做的事情,之所以要发明二重指针(函数指针、数组指针),就是为了让编译器了解这个指针被定义时定义它的程序员希望这个指针被用来指向什么东西(定义指针时用数据类型来标记,譬如int *p,就表示p要指向int型数据),编译器知道指针类型之后可以帮我们做静态类型检查。编译器的这种静态类型检查可以辅助程序员发现一些隐含性的编程错误,这是C语言给程序员提供的一种编译时的查错机制。
(3)为什么C语言需要发明二重指针?原因和发明函数指针、数组指针、结构体指针等一样的。
3.2 二重指针的用法
(1)二重指针指向一重指针的地址。
(2)二重指针指向指针数组的。
(3)实践编程中二重指针用的比较少,大部分时候就是和指针数组纠结起来用的。
(4)实践编程中有时在函数传参时为了通过函数内部改变外部的一个指针变量,会传这个指针变量的地址(也就是二重指针)进去。
3.3 二重指针与数组指针
(1)二重指针、数组指针、结构体指针、一重指针、普通变量的本质都是相同的,都是变量。
(2)所有的指针变量本质都是相同的,都是4个字节,都是用来指向别的东西的,不同类型的指针变量只是可以指向的(编译器允许你指向的)变量类型不同。
(3)二重指针就是指针数组指针
总结
(1)定义一个符号的关键在于:首先要搞清楚你定义的符号是谁(第一步:找核心);上面三个例子的核心都是p。其次,再来看谁跟核心最近、谁跟核心结合(第二步:找结合);之后继续向外扩展(第三步:继续向外结合直到整个符号完)。如果核心和*结合,表示核心是指针;如果核心和[]结合,表示核心是数组;如果核心和()结合,表示核心是函数。跟谁最近就是跟谁结合,但是如果核心左右两边都有符号怎么办?这个时候考虑优先级的问题,谁的优先级高就是跟谁先结合。
总结1:优先级和结合性是分析符号意义的关键
在分析C语言问题时不要胡乱去猜测规律,不要总觉得c语言无从捉摸,从已知的规律出发按照既定的规则去做即可。
总结2:学会逐层剥离的分析方法
找到核心后从内到外逐层的进行结合,结合之后可以把已经结合的部分当成一个整体,再去和整体外面的继续进行结合。
总结3:基础理论和原则是关键,没有无缘无故的规则