1.指针的本质
1.1内存单元、地址、指针的关系
在计算机中,运行的程序和数据都是放在计算机内存中,内存的基本单元是字节,一般存储器中的一个字节被称为一个内存单元,不同数据类型所占据的内存单元不等,如整型变量占两个单元(有的编译器为4个),字符变量为1个单元等,为了正确的访问内存单元,必须给每个内存单元一个编号,该编号称为该内存单元的地址。程序中的每个变量都有固定的位置,有具体的地址。
指针是访问变量的地址,过去我们常用的scanf函数,其实是一种直接访问的方法,可以理解为直接取地址进行操作的方式为地址的直接访问,访问变量时需要先访问其地址,如果用一个变量来存储访问变量的地址,那这个变量就是地址变量--->没错,就是我们的主角指针变量了!通过指针访问指向变量的方式称为“间接访问”,访问变量的地址就是指针,存放指针的变量是指针变量,这两个概念不要混淆了。
为什么我们明明可以用地址去表示,却还要引入指针这个概念呢?问的很好,因为在实际的程序运行过程中,一个函数或者一个数组是占据了一大片地址,这个时候我们用地址去描述函数或者数组对象就会显得很麻烦,但是指针指向了函数或者数组的首地址,我们通过首地址便可以访问这个数组或者函数了,这样一种“数据结构类型”被一个指针完美表达了,对于我们的实际调用和访问简直太棒了!ok,看到这里想必你肯定已经明白了指针本质了。
1.2指针变量的定义及运用
在C语言中,指针变量和其它变量一样,在使用前必须先定义(不然就是野指针了很危险的),定义的格式为 (类型说明符 *变量名)---->(int *ptr 指向整型变量的指针变量)。
指针赋值有两种常见方法,第一对指针变量初始化,第二是用赋值语句。C语言中,变量的地址是由编译器系统自动分配的,对于用户来说是完全不透明的,因此必须使用地址运算符&取得变量的地址。
指针变量的引用形式为 (* 指针变量),其中“*”为取内容运算符,为单目运算符,其结合性为右结合性。注意哦,这个“*”运算符后面的变量只能是指针变量,在指针变量说明中,"*"是类型说明符,表明这是个指针变量,而表达式中的“*”则是指针运算符哦,不要搞混了。
指针有赋值运算、加减法运算、关系运算、空运算。
//赋值运算
int *p, a = 2;
p = &a;
//加减法运算
int a[5], *pa = a;
pa += 2; // P = P + 2, p之前指向a[0], 加法运算后 p指向a[2]
//关系运算
int *p1, *p2, a1=1 , a2=2;
//若运算成立
p1 < p2; //p2指向地址更大
p1 > p2; //p1指向地址更大
p1 == p2; //p1与p2指向地址相对
//空运算
int *pm = NULL;
2.指针与数组
2.1指针与一维数组
众所周知,数组名等于数组的首地址等于数组第一个元素地址(欧克,这里你们先知道,我告诉你们的),所以数据名就可以等价于数组的指针,因此我们常用指针指向数组名,例如:指针ptr就获取到了数组的首地址, ptr = arrays = &arrays[0]。 接下来我们将进行指针与数组的访问,结合之前的*ptr等于取出地址变量的概念,我们可以得出以下关联式*(ptr+n) = arrays[n] = ptr[n];这个公式非常的实用,请务必记住。
int arrays[10] = {0};
int *ptr;
ptr = arrays;
2.2指针与二维数组
二维数组可以理解是行列组合,如a[2][3]表明2行3列总共6个数组元素,在内存中按行存放,其中a是二维数组的首地址,a+i = &a[i][0] = a[i] = &a[i] = *(a+i) ;这里可能有小伙伴发出灵魂拷问,a[i]这么理解呢?很好,a[i]可以理解为第i行的首地址,所以它肯定和a是等价的,因为a也是第i行的首地址,二维数组可以理解为一维数组的集合,是不是这样就恍然大悟了。同理对于访问可以得出下面的关联式:a[i][[j] = *(a+i)[j] = *(*(a+i)+j)。
int a[3][4];
int *p1; //一维变量
int (*p2)[4]; //二维数组指针变量
P1 = a[0];
p2 = &a[0][0] = a;
2.3指针与多维数组
多维指针这里就是在二维基础上叠罗汉了,实际情况几乎用不到三维数组和指针的情况,一方面不方便代码的移植理解,另一方面大大增加了指针误用的概率和风险,这里就不多介绍了。
3.指针与函数
函数相比大家已经非常清楚了,我这边就简单介绍一下,函数的定义是通常会有形成,比如代码中,函数定义的入口(int a, int b)为形参,mian函数中调用的函数test_fun(a,b),这里的a,b为实参,当形参是指针类型时,这个时候实际是向函数传递变量的地址,而不仅仅是函数的值了,也就是常说的地址传递和值传递。
注意C语言中,函数的实参与形参始终是“值传递”的方式,只是当形参为指针变量时,我们不能改变指针变量本身的值,但可以通过函数改变指针变量指向的变量值,这并不冲突。函数可以返回一个值,而运用指针变量作为参数,可以得到多个变化的值,这就是参数为指针变量的好处。
指向函数的指针,每个函数在编译链接后会占用一段连续的内存区,函数名就是该区域的入口地址,每个入口地址就是函数指针。定义函数指针,可以提高程序的通用性和适应性,常用于回调函数中。函数指针的定义为 类型说明符 (* 指针变量名)()--->( int (*pf)() ) , pf为函数指针。
int test_fun(int a, intb)
{
int c = 0;
int c = a+b;
return c;
}
int mian()
{
int a = 2;
int b = 4;
int c = 0;
c = tese_fun(a, b);
}
4.指针小结
1.变量的地址就是指针变量;
2.指针运算符在定义是类型符合,在表达式中是间接取值;
3.C语言中,绝大部分变量都可以用指针指向,取值的方法有两种,一个是变量名直接取值,一个是指针变量间接取值;
4.指针变量指向一维数组时,p+1指向下一个元素,p-1指向上一个元素;
5.指针数组的元素是指针;
6.理解指针函数与函数指针;
7.理解指针数组与数组指针;
8.指针的指向一定要明确,防止越界;