指针与数组的关系及运算

 

一、概念:

1.1、指针数组(char*p[4]):表示p是含有4个元素的数组,每个元素存放的数据是指针类型。

1.2、数组指针(int(*p)[4]):表示p是个指针,p指向地址(p值)的数据空间长度为4个int。

1.3、指针与数组的关系:

其实指针的本质就是数组,一级指针是一维数组,二级指针是二维数组,以此类推。注意: “[]”与“*”都是取数据符号,但“[]”取得是数组自身元素的数据,而"*"取得是间接取p值的数据,(也就是变量a的值)。

int *p         \\即p指向地址在内存的数据长度为1个int ,注意int*p与int(*p)[1]完全不一样,相当于变量和数组的区别。

int (*p)[1]    \\表示p是个指针,p指向地址(p值)的数据空间长度为1个int(只有1个元素的数组,它与变量还是有区别的),此时将p指向地址的数据空间看作一个数组。该指针定义中有1个“*”和1个“[]”两个取数据符,也就是说表达式p中含有两个取数据符结果才是数据。

int (*p)[4]                   \\表示p是个指针,p指向地址(p值)的数据空间长度为4个int,该数据空间看作含4个元素的数组。如p[2]表达式的值为地址,表示指针p指向地址偏移2个单位(int(*)[4]即2x4字节后在内存中的地址;如p[2][3]表达式为数据,表示指针p指向地址先整体偏移2个单位(int(*)[4])即8字节,再偏移3个元素,即*(p+2)+3的地址,取该地址在内存中的数据即*(*(p+2)+3)=p[2][3]。

注意:类型相同长度不同的指针,不能直接赋值,如下

int a[3]={0};    \\数组a长度为3个int。

int *p=Null;

p=a;   \\等价于p=&a[0],此时的a代表第一元素的地址。

p=&a    \\此时a代表整个数组(长度3个int)的地址,这种写法是不规范的,正确写法:p=(int*)&a

1.4、指针变量p是个地址,而数组的数组名a也是个地址,从而可以这样赋值p=a,由此衍生的指针数组、数组指针所产生的表达式及运算千变万化,经常看到一个表达式都不知道,最终所求的值是地址还是数据。网络上看了好多推演的教程,也思考了很久,但看到好多推演断层式的表达式,还是想不明白。最终自己找到“以不变应万变”的方法,算是自己的感悟吧!

二、“3部曲”

部曲1:判断p在定义的指针表达式中是指针变量还是数组。用优先级原则判断,即p先跟“*”还是“[]”结合,前者p为指针变量也就是地址,后者p为数组名,以数组对待。

部曲2:数组的表达式中“[]”的个数是否等于维数,等于则表达式就是元素值,不等于则表达式的值都是地址,无论多少维数组。验证如下:

(1)

 不管多少维的数组,表达式中"[]”比维数只要少一个,那么这个表达式的值就是地址,并且该地址是该维的首地址,如二维数组a[3][2],三维数据b[2][3][2],一维数组c[10]。

(2)

任何维数数组所表示的地址表达式("[]”比维数少),增加"[]"结合成新表达式时(结合“*”相当于增加一个"[]"),只要“[]”的个数少于维数,无论怎么结合都是地址偏移(有多少个“[i]”或“*”要结合,那么直接加i就行),并且它结果还是个地址,只有当“[]”的个数等于维数,表达式的值才是元素值(也就是该内存地址中的数据)。注意: “[]”与“*”都是取数据符号,等效的。


部曲3:p+1偏移代表什么。若p在指针定义表达式中是数组,则偏移一个元素;若p在指针定义表达式中是指针变量,则偏移指针定义中指向的整个数据的长度。

三、案例分析

案例1(指针数组):

int n[3][4] = { 1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12 };

int *pn[3] = { n[1], n[0], n[2] };       \\pn先与[]结合,所以pn为一维数组。

printf("%d\n", (*(pn + 2))[3]);        \\数组pn少[],pn为地址且偏移2,取数据符*(pn+2)等价pn[2],即一维数组pn结合1个"[]"就是元素值(数据),因pn[2]的值为n[2],而n为二维数组,n[2]少了1个"[]"表示为地址,n[2]在结合1个“[]”即n[2][3]=12表示元素(数据)。

printf("%d\n", pn[1][2]);                \\数组pn结合1个"[]"即pn[1]就是元素(数据),因pn[1]的值为n[0],而n为二维数组,n[0]少了1个"[]"表示为地址,n[0]在结合1个“[]”即n[0][2]=3表示元素(数据)

printf("%d\n", *(pn + 1)[1]);          \\数组pn少[],pn为地址且偏移1,(pn+1)没有[ ]还是个一维数组的地址,跟"[1]"结合,既结合“[]”又偏移1,满足“[]”个数 等于维数,即为pn数组的元素pn[2],因pn[2]=n[2],而n为二维数组,n[2]少了1个"[]"表示为地址,与“*”结合,相当于增加一个"[]",即*n[2][0]=9。这里要特别注意运算优先级。

 

案例2(数组指针):

   int a[14] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14 };

   int(*p)[2] = a;               //p先与*结合,所以p是指针,长度为2个int。从指针的定义看,指针有两个取数据符,也就是说表达式中含有两个取数据符结果才是数据,类推int(*p)[2][3]要3个取数据符结合才是数据。


    printf("%d\n", p);            //p的不够2个取数据符,为地址。
    printf("%d\n", *p);           //p的不够2个取数据符,为地址。
    printf("%d\n", **p);          //p结合2个取数据符,为数据。
    printf("%d\n", p[1]);         //p的不够2个取数据符,为地址。
    printf("%d\n", *p[1]);        //p结合2个取数据符,为数据。
    printf("%d\n", p[1][1]);      //p结合2个取数据符,为数据。
 

 

 

 

案例3(数组指针):

int n[3][4] = { 1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12 };

int  (*pn)[4]=n;                               \\pn与*先结合,因此pn为指针变量,并且指向的数组长度为4。等价式pn=n,即可将pn当做二维数组n运算。

printf("%d\n", (*(pn + 2))[3]);         \\pn偏移2单位还是地址,再与*结合(相当于pn增加一个"[]"),即pn[2]=n[2],最后n[2]和[3]结合即n[2][3],所以n[2][3]=12是数组的元素。
printf("%d\n", pn[1][2]);                 \\pn=n,即pn[1][2]=n[1][2],而n[1][2]中“[]”个数等于数组维数,n[1][2]=6为数组元素。
printf("%d\n", *(pn + 1)[1]);           \\pn偏移1单位还是地址,跟"[1]"结合,既增加“[]”又偏移1,即pn[2],因pn[2]=n[2],而n为二维数组,n[2]少了1个"[]"表示为地址,与“*”结合,相当于增加一个"[]",即*n[2][0]=9。

 

 

 

 

 

  • 13
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值