什么是指针?
指针是一类变量,与其他变量不同的是它存放的值是地址。
int *a;//表示a是一个指向int类型数据的指针,在定义时*作为类型说明符说明他是一个指针类型的变量。
在使用a时要注意a的值是一个地址,而*a的值是一个int类型数据地址上的值,此时星号是一个指针运算符,用来引用一个int类型数据地址上的值。这时候的&a则代表取存放指针变量a的存储单元的地址,&为取地址运算符。
·注意定义后的指针变量最好初始化他,因为不初始化时指针指向一个不确定的存储单元,该存储单元可能存放一个有用的数据。
·当指针作为函数的形式参数时,该形式参数与实际参数都指向同一个存储单元。形参通过改变该存储单元的值时实参的值同样改变,这是地址传递。而普通变量间的值传递相当于把实参的值复制给形参使用,不会影响实参的值。
指针的运算
指针加或减一个整型变量n(抑或是一个整型的表达式)相当于让指针前进或后退n个位置。而这个位置长度由指针的基类型决定,基类型有多少个字节,前进一个位置便是多少个字节。
·如果两个指针相加,这没有意义。但在一个数组中两个指针相减可得出指针指向的元素之间相差了多少个位置。
·不能把一个整数赋给一个指针变量,系统不会把它当成地址。
通过指针引用数组元素
int a[10];
int *p=a;
1.下标法:a[i]形式。
2.指针法:*(p+i)形式。把p改为a同样可行,因为数组名是数组的首地址。
a[i]形式在系统内部同样需要变为指针形式,而直接使用指针形式可省略该步骤,运行速度更快。但要注意:p++是合法的,但a++不合法,因为a是一个地址常量不能改变,而p是一个指针变量可以改变。
函数与指针
·想要把地址引入函数之中,函数形参就需要一个指针变量接收实参传来的地址。而以数组作为形参时,并不会对数组的长度进行检查,因为系统也把该形参当作一个指针处理,所以形参的数组名等于指针,它的值可以被改变。
多维数组的地址
定义一个二维数组
int a[10][10];
此时a为整个二维数组的首地址。
让我们回顾一下,若有定义int a[10],a[0]是数组第一个元素的值,*a也是数组第一个元素的值,这说明a[]的作用与指针运算符作用高度相似。让我们再把它扩展到二维数组。二维数组可以看作是一个一维数组里每个元素也都是一个数组,所以我们也容易得出数组的一个内存摆放的规律==数组内存的排列是横向把元素依次排列,即二维数组a[10][10]中首地址后面排列的是a[0]中10个元素的存储单元。这同样可以扩展到多维数组。
·二维数组指针表现形式有多种,指向第一维的一般被称为行指针,第二维的一般称为列指针。
1. 行指针:a,a+1,&a[0],&*a等
2. 列指针:a[1],*a,a[1]+1,*(a+1)+2,&a[1][1]等
3. 值:**a,*(*(a+1)+1),a[1][1]等
理清这些最重要是看如何理解*,&和[]。指针运算符和[]相当于进入一个维度,&相当于退出一个维度。行指针通过星号变为列指针,列指针通过&变为行指针。
指针数组与数组指针
int *p[10];
int (*p)[10];
根据优先级判断,[]为第一优先级,*为第二优先级,所以p在没有括号时会先和下标结合形成指针数组,第二个由于括号的存在p先和星号结合形成了指针再和下标结合形成数组指针。
·指针数组:数组元素都是指针变量。
·数组指针:指向数组地址的指针(指向二维或多维数组中的一维数组)。
用指针引用字符串
char* p="aaa";
此时把字符串的首字符地址赋予了p。
而如果想实现从键盘输入字符串给指针变量,这时无法像上方代码一样系统分配内存给字符串,所以需要我们自己先开辟一个内存空间把其地址给予指针,我们再输入字符串才不会报错。
指针变量不管什么类型所占的存储单元是固定的,不会随基类型改变而改变。
函数指针
函数名和数组名一样也是函数的首地址,也是一个常量。
int (*p)(char,int);
上方代码定义了一个函数指针,指针指向的函数需要一个char类型和一个int类型的参数,且函数返回一个整型值。
同样需要留意优先级问题,p括号不能缺少,不然会变为一个返回指针值的函数。
在使用时*(p+1)并不是函数中下一条语句的意思。
在牛客遇到的一道题:
声明一个指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,参数是int*。
首先先声明一个数组指针:(*a)[10],我们把它看作p,则题目便可以简化成
p是一个函数指针,该函数的返回值是int,参数是int*。
我们写出p:int (*p)(int *)
再把p代换就可以得到
int (*((*a)[10]))(int *)
//有了新的感悟会再来增加。