指针
指针就是地址
主要说几个注意点
1.指针的类型必须一一对应,int型的指针就必须对应int型的变量
不是一一对应的话就会报错
2.指针的加法和减法
假如说是一个int型的指针,对指针进行+1或者-1,在地址上加减的是一个sizeof(int)
char arr[3];
printf("arr:\n%d\n%d\n%d\n", arr, arr + 1, arr + 2);
char *parr[3];
printf("parr:\n%d\n%d\n%d\n", parr, parr + 1, parr + 2);
1.指针对数字,注意是数字,进行加减,必须是连续的空间有意义,也就是数组,不连续得到空间上进行加减数字没有任何意义
2.两指针进行减法,指针没有加法!指针的减法=(地址1-地址2)/sizeof(类型)
3.注意千万别越界
为什么没有报错?
C语言非常重视运行时的效率,所以没有进行数组越界检查。(检查数据越界,编译器就必须在生成的目标代码中加入额外的代码用于程序运行时检测下标是否越界,这就会导致程序的运行速度下降)
数组越界后,会自动接着前面那块内存往后写,这样带来的将会是一系列安全问题。因为界外的内存不确定是否已经存放了东西,如果不凑巧存放着比较重要的数据,那么数组越界后将会把这块内存上的重要数据替换掉。
指针计算中易错表达式
int arr[5] = { 1,3,5,7,9 };
int *p = arr;
*++p:p先自 + ,然后 * p,最终为3——马上改变指针, * 下一个指针
*p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1] ————之后改变指针,*原数据,指针++
*(p++):效果等同于 * p++ ——之后改变指针, * 原数据,指针++
( * p)++:先 * p,即arr[0]=1,然后1++,该语句执行完毕后arr[0] =2——不改变指针,数据++
++ * p:先 * p,即arr[0]=1,然后再++,最终为2——不改变指针,++数据
总结:
只有++(*p),(*p)++,++*p使指针的位置不变,改变数组的原始数值,
其他的任何情况都是指针指数组的下一个,指针的位置+1
(感谢有个兄弟私信我说这里没讲清楚,如果有错误或者不清楚的地方欢迎大家提出)
3.指针和指针变量是两个不同的概念
指针:地址
指针变量:是一个指针变量,这个变量的值是地址
例子:
int a[10],这里的a可以看作是指针常量,值不能发生改变,所以a+=1和a++都是错误的,但是我们int *p=a,然后对p进行++操作就没有问题!这里的a可以看作是指针常量,值不能发生改变。但是我们的p是指针变量
4.指针作为函数参数
普通的值传递都是单项传递,传递指针过去则是双向传递
情况一:
我们的传递值就对应上面那张代码图。
再看看另外一种情况
这个地方为什么a,b的值没有改变。
也就是说,我们值改变了形参的指向,并没有改变实参的指向。
5.const和指针的关系
讲得很清楚了,可以看看,点击传送
6.二维数组和指针
行指针格式:int (*a)[5]
行指针,顾名思义就是指向一行的指针。那么哪里会用到行指针呢,用的最多的,那就是二维指针了,大家都知道,我们通常把二维指针看成一个行列式,但是它在内存中的排序却是和一维指针一样的。
比如组a[2][3]={{1,2,3}{4,5,6}},a是整个数组的首地址,同时也指向第一行元素,即a是一个行指针,它每加1,所指地址移动二维数组的一行,a+1指向第二行元素。
对a取*,即a指向第一行第一个数,(a+1)指向第二行第一个数,可见,对行指针取值就成了列指针,此时它还是个指针。它每加1,所指地址移动一个元素,*(a+1)+1指向第二行第二个元素,也可以写成a[1]+1。
a(也可写成a[0][0])就是这个二维数组第一行的第一个元素,(a+1)(也可写成a[1][0])就是第二行的第一个元素,((a+1)+1)(也可写成a[1][1])是第二行的第二个元素。可见,对行指针取2次*就成了某个元素的值了,而不再是地址。
有这样一个公式a[i][j]=((a+i)+j),应该就知道为什么了吧…
行指针还可以这样定义int (*p)[n],此处定义的p指针,每加1,移动n个地址位(针对不同二维数组定义不同的n值)比如下面的这段:
int main(void)
{
int a[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3];
p = a;
p++;
printf("%d",**p);
}
此段代码输出结果为4,p是个行指针,可以直接将a这个行指针直接赋值给它,此时p也指向二维数组的起始地址,即第一行。p++就会移动3个元素,从而指向第二行。用法同二维数组名a。
对于二维数组中,因为我们的操作中没有取一行这个说法,所以我们的*a的这个 * 的作用可以理解为指针转化,将行指针转化为列指针
7.函数指针和返回指针的函数
1.指针函数:
返回型 (*变量名)(形参列表) { … }
2.返回指针的函数:
类型名 *变量名(形参列表) { … }
8.写法汇总
int* a;
int* a[5];
int (*a)[5];
int (*a)();
int *a();
|