指针与数组的关系及运算

 

一、概念:

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
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 这是一段C语言代码,主要涉及指针数组关系以及指针运算。 首先,定义了一个整型数组arr,长度为5,其中arr[0]~arr[4]分别为1、2、3、4、5。 然后,定义了一个指向整型数组指针p,将p指向arr数组的首地址,即p=&arr[0]。 接着,通过指针p访问数组元素,即*p表示arr[0],*(p+1)表示arr[1],以此类推。 最后,通过指针p修改数组元素的值,即*p=10表示将arr[0]的值修改为10。 可以看出,指针p与数组arr之间存在着密切的关系指针p指向了数组arr的首地址,通过指针p可以访问数组元素,也可以修改数组元素的值。指针运算中,p+1表示指针p向后移动一个元素的距离,即指向arr[1]的地址,p-1表示指针p向前移动一个元素的距离,即指向arr[-1]的地址,但是由于数组下标从0开始,因此p-1是非法的。 总之,指针数组是密不可分的,指针可以通过指针运算访问数组元素,也可以修改数组元素的值,这是C语言中非常重要的概念。 ### 回答2: 以下代码是一个简单的C语言程序,它定义了一个整数数组和一个整数型指针变量,并且通过指针变量来访问数组元素进行赋值操作。 ``` #include <stdio.h> int main() { int arr[3] = {1, 2, 3}; int *p = arr; int i; for(i = 0; i < 3; i++) { *(p + i) = 10 * i; /* 设置数组元素的值 */ } for(i = 0; i < 3; i++) { printf("%d ", arr[i]); /* 输出数组元素的值 */ } return 0; } ``` 在该程序中,通过定义一个整数数组“arr”,并且给这个数组初始化为{1,2,3},我们可以访问数组的每个元素及其值。同时,在程序中定义了一个整数型指针变量“p”,并且将其初始化为指向数组的第一个元素“arr[0]”(即“p = arr”)。通过指针变量“p”访问数组元素时,我们可以使用指针运算符“*” 得到指针所指向元素的值。 在程序的第一个循环中,使用“*(p+i)”来访问数组元素的值,并且对元素的值进行修改,如果我们假设在循环的第一次迭代中,指针变量“p”的初始值为“arr[0]”,则“*(p+i)”相当于“arr[i]”(即p+i所指向的元素),也就是说“*(p+i)”语句最终能够设置数组元素的值。在循环结束后,数组元素的值被设置为{0,10,20}。 在程序的第二个循环中,我们使用“printf”函数将数组元素的值输出到控制台。这里的循环和第一个循环的区别在于对数组元素进行访问的方式不同(前一种是通过指针变量“p”以指针运算的方式访问数组元素,而后者是用数组的下标访问数组元素)。程序的输出结果将会是:“0 10 20”。 在本程序中,指针变量“p”和数组名“arr”是非常重要的概念。由于数组名被视为数组首元素的指针,因此将指向数组首元素的指针赋值给指针变量“p”意味着“p”可以访问数组中的所有元素。当然,在C语言中,指针变量和数组之间也有一些重要的区别。例如,定义一个指针变量只需要使用一个“*”符号声明,而数组的声明必须要定义其长度,所以在使用指针变量操作数组时,需要注意指针的使用方式和特性。 综上所述,程序中的代码使用了指针数组几种重要的概念,通过指针变量操作数组元素并修改元素的值,最后输出修改后数组的值。在学习C语言时需要充分理解某些概念与其他关键概念的相互作用,这样才能更好地掌握这门语言。 ### 回答3: 这段代码主要是循环遍历一个整型数组,找到数组中的最小值并返回其下标。 对于指针数组关系,可以先将数组的定义和指针的定义进行比较。数组是一个由连续存储的相同类型数据元素组成的有序集合,可以通过下标访问数组元素。而指针是一个变量,它的值为另一个变量的内存地址,指针可以通过间接访问来访问被指向的变量。 在本段代码中,指针数组关系体现在函数参数声明处。在C语言中,将数组作为函数参数传递时,实际上传递的是数组的首元素地址,因此参数列表中可以用指针来表示数组指针运算方面,我们可以看到在循环中通过指针和下标遍历数组指针p指向数组的首元素,通过++p来实现指向下一个元素的指针。而在计算最小值时,我们通过比较p所指向的元素和当前最小值来更新最小值,并记录当前最小值的下标。在这里,指针与下标的结合既能够遍历整个数组,又能够实现元素的比较和查找,提高了代码的效率。 总的来说,指针数组在C语言中密不可分,它们的联合使用能够发挥出强大的功能,带来很多方便和高效的编程体验。理解指针数组关系指针运算是C语言编程中一个重要的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值