引言:
从开始学习C语言至今,我们谈论指针的次数已经快数不清了,可是很遗憾,作为C语言的灵魂,指针老铁并没有那么容易被征服,不过这次,我们努力征服它!
作者:小 琛
欢迎转载,请标明出处
本文将谈到的内容:
1、字符指针
2、指针数组、数组指针
3、数组和指针作为参数
4、函数指针、函数指针数组、指向函数指针数组的指针
我相信你已经头大了,不过世上无难事,只怕有心人,接下来我们开始
字符指针
我们直接上代码再分析
#include <stdio.h>
int main()
{
char str1[] = "hello.";
char str2[] = "hello.";
char *str3 = "hello.";
char *str4 = "hello.";
//*str3 = "qw"; 错误语句,字符串指针在read only区,不可修改
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are different\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are different");
return 0;
}
上面的代码,我们看到:笔者定义了四个存放字符串的变量,两个数组和两个指针。而后续的判断中,两个数组进行比较、两个指针进行比较,看他们是否相等,先看结果
也就是说,两个数组是不相等的,而两个指针则相等!为什么呢?
这是因为:
1、当我们定义数组存放字符串的时候,相当于单独开辟一块内存,这块内容的内容就是这个字符串,而在前面的学习中我们知道“数组名的意义有一条就是数组名代表数组首元素的地址”,也就是说,在进行(str1== str2)的判断时,其实是两个不同内存的数组首元素地址的比较,那么他们定不相等!
2、而定义了两个指针指向字符串的时候,注意:定义的指针指向字符串并不是说将字符串的内容就给了指针,这在逻辑上也是行不通的,编译器做的其实是将字符串首元素的地址给了这个指针,从此以后,这个字符指针的指向确定即就是这个字符串首元素的地址! 我们再对这两个指针进行解引用比较,(*str3== * str3),相当于是两个相同地址比较,因此相等!
3、代码中注释掉的一句是另一个知识点:数组的内存开辟是再栈上进行,而字符指针的开辟是在read only(只读区),因此意味着数组是可以修改的,而字符指针的修改则会报错
指针数组和数组指针
首先是对于这两者本质的区分,笔者分享一个窍门:只看后两个字,后两个字是什么那它就是什么例如指针数组后两个字是数组它就是一个数组,存放指针的数组,而数组指针后两个字是指针,它就是一个指针,指向数组的指针。
他们二者的初始化:
int*arr1[10];//整形指针的数组
char*arr2[4];//一级字符指针的数组
char**arr3[5];//二级字符指针的数组
int(*p)[10];
//p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针
//()优先级最高,*优先级低于[],因此加小括号实现
&数组名和数组名
在这个标题下穿插一个知识点,就是&数组名和数组名的差别
看代码:
#include<stdio.h>
int main()
{
int arr[10]={0};
printf("arr=%p\n",arr);
printf("&arr=%p\n",&arr);
printf("arr+1=%p\n",arr+1);
printf("&arr+1=%p\n",&arr+1);
return 0;
}
结果如下:
分析:首先,如果直接输出&数组名和数组名,他们是没有差别的,而我们在对它们分别进行加1操作后,差别就显示了出来
arr+1即数组名+1代表:对数组的成员加了1
而&arr+1则不同,实际上&arr代表的是整个数组的地址,因此加1相当于加了整个数组个大小
数组和指针作为参数
1、二维数组传参,函数形参的设计只能省略第一个[]的数字。
因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。这样才方便运算。
2、void print_arr2 (int (*arr)[5],int row,int col),对于数组指针,我们通常用在传参,二维数组传参的时候其实是传的一个一维数组的地址,因此数组指针就可以作为形参名
函数指针、函数指针数组、指向函数指针数组的指针
是时候看重头戏了,这是本文的难点,笔者在学习的时候也是痛苦万分!
函数指针
我们先看一段代码:
#include<stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n",test);
printf("%p\n",&test);
return 0;
}
结果如下:
输出的是两个地址,也就是说,我们所定义的函数,它是有自己独立的存储单元的,那就意味着我们可以使用指针来指向它。对比前面讲的数组指针的写法我们可以尝试写:
void (*pfun1)();
*其中,小括号优先级最高, 先和pfun1结合,代表是一个指针,指针的指向是一个返回值为void 参数为()即为空的函数
函数指针数组
函数指针数组,故名思意,本质是一个数组,该数组用来存放函数指针。
例如:
int (*parr1[10]) ();
首先,括号优先级最高,先看小括号从里到外。小括号内中括号优先级最高,率先和parr1结合,代表它是一个数组;再与“ * ”结合,代表该数组存放指针;而存放什么指针呢,看最外层,它存放返回值int类型、参数为空的函数指针。
指向函数指针数组的指针
如果是直接看到这个东西,估计90%的人都会崩溃,但由于我们是循序渐进的讲述,它就并不是那么夸张的难。首先拆分它,看最后两个字,它是一个指针;一个指向什么的指针呢?继续往前看,是一个指向数组的指针;而这个数组又是什么数组呢?是一个存放函数指针的数组。
例如:
void( * ( *ppfunArr)[10])(const char *)=&pfunArr;
我们看到这个东西,同样不必害怕,照猫画虎从里到外一步一步进行!
首先看优先级最高的内部小括号,内容是 *ppfunArr,代表它是一个指针;是一个什么指针?接下来中括号优先级最高,结合后表示它是一个指向数组的指针;而这个数组是什么数组?继续往外看,是一个存放指针的数组;那是个什么指针?是参数为const char *、返回值为void的函数指针;
有关指针的理论知识到这里就全部讲述完成了,实际上引入的新知识仅仅是函数指针与它的后续内容,只要掌握了它的规律抓住它的窍门,所有的东西都是非常简单的。接下来博主还会推出一篇针对指针的练习篇目。