408学习笔记-8-C-指针(2)

2、指针与数组

2.1、数组名的理解

在研究数组名之前要明晰一个点:在C语言中虽然没有对象,但是我们定义的变量是具有对象的结构的,即一个变量不止单纯地存放一个值,还有其他诸多属性。

定义一个数组变量,例如:

int a[5] = {0};

通过&数组变量名我们可以得到这个数组变量在内存中的指针,根据之前所学可知指针是具有内置信息的:这个指针的所指向的对象的数据类型为:5元素整型数组,所指向对象的量级为4*5byte

可是,数组变量名的特点就在于,它内置了一个指向数组中首个一阶元素的指针,这个指针是一个地址字面量而非指针变量,即不可修改、赋值,这个指针的内置信息为:所指向的对象的数据类型为:int,所指向的对象的量级为4byte

也就是说,单纯输出数组变量名,与输出&数组变量名,得到的是两个不同的指针。


2.2、数组名的退化与升维

以下两种情况下,数组变量名会被视作整个数组:
1、sizeof(数组变量名),即sizeof()单独作用于数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
2、&数组变量名,即&单独作用于数组名,这里的数组名表示整个数组,取出的是指向整个数组的指针。

除了以上两种情况,数组变量名都被视作指向首个一阶元素的指针(不是指针变量!内置的这个指针的值不可改变!)。特别注意一种情况:sizeof(数组名参与的运算式),例如:sizeof(数组名+1),此时sizeof()作用于运算式数组名+1的结果,故而此时数组名是被视作指向首个一阶元素的指针来参与的。

其实以上的根本原因是:
1、*操作符对数组指针弱化作用,即:会把指向数组的指针弱化为指向此数组首个一阶元素的指针。

#include <stdio.h>


int main()
{
    int arr[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};

    //arr为指向首个一阶元素{0,1,2,3}的指针,也就是数组指针
    printf("%p\n",arr);//000000000061FDF0

    //由于{0,1,2,3}的量级为4*4=16,故而偏移步长为16
    printf("%p\n",arr+1);//000000000061FE00


    
    //*a使得数组指针arr弱化,弱化为指向{0,1,2,3}的首个元素0的指针
    printf("%p\n",*arr);//000000000061FDF0

    //由于0的4,故而偏移步长为4
    printf("%p\n",*arr+1);//000000000061FDF4


    return 0;
}

2、sizeof()&这两个操作符仅对数组名中内置的指针具有升维作用,即:会把数组名中内置的指针视作指向整个数组的指针。(仅对数组名中内置的指针有此作用,对其他指向首个一阶元素的指针无此作用)。

#include <stdio.h>


int main()
{
    int arr[5] = {0};

    printf("%zd\n",sizeof(arr));//20

    printf("%zd\n",sizeof(&arr[0]));//8

    printf("%zd\n",sizeof(&arr[1]));//8

    return 0;
}

2.3、通过数组名访问元素的实质

因此可知通过数组名访问元素:arr[n],它的实质就是:*(arr+n);也可以int* p = arr,再以p[n]的方式访问元素。

再探究深一点,以arr[n]的格式访问元素,[]是双目操作符,那么就有两个操作数:arrnarr[n]这种访问格式在编译阶段都会转化为*(arr+n),因此arrn这两个操作数的前后顺序是不影响结果的,故而*(arr+n)可写成*(n+arr),那么arr[n]也可写成n[arr]


2.4、一维数组传参的本质

我们知道,一维数组传参的格式为:函数名(数组名),实质上作为实参的是数组名内置的指向数组内首个一阶元素的指针。
如下代码:

#include <stdio.h>


void test(int arr2[])
{
	int sz2 = sizeof(arr2)/sizeof(arr2[0]);
	printf("sz2 = %d\n", sz2);
}

int main()
{
	int arr1[10] = {1,2,3,4,5,6,7,8,9,10};
	int sz1 = sizeof(arr1)/sizeof(arr1[0]);
	printf("sz1 = %d\n", sz1);
	test(arr);

	return 0;
}

test()函数的形参定义格式为:int arr2[],注意,这里的形参不是数组!而是一个指针,写成这样类似数组的格式是为了方便使用,当我们使用sizeof(arr2)得到的返回值是8byte,也就是指针变量的量级。


2.5、二级指针

指针变量也是变量,是变量就有地址,二级指针变量就是用来存放一级指针的地址的。

例如:指针变量A的地址为&A,用另一个指针变量B来存放&A,即让指针变量B指向指针变量A,则指针变量B就是一个二级指针。

代码如下:

int a = 1;
int* pa = &a;
int** ppa = &pa;

ppa就是二级指针,注意二级指针的定义方式int**,格式中后面部分的*指明当前定义的变量是一个指针变量,前面部分的int*指明当前这个指针所指向的对象的数据类型是整型指针,所指向的对象的量级为8byte

通过对paa单次解引用*paa可得到pa,双次解引用**paa可得到a

n级指针以此类推。


2.6、指针数组

指针数组是指元素为指针的数组。

例如:

int arr1[4] = {0,1,2,3};
int arr2[4] = {4,5,6,7};
int arr3[4] = {8,9,10,11};

int* arr = {arr1,arr2,arr3};

这里的arr就是一个指针数组,而arr还内置了一个指向首个一阶元素的指针,而这个数组的一阶元素的类型为int*,所以arr内置的指针是一个指向整型指针的二级指针


2.7、数组指针变量

首先要明晰一个知识点:
数组的数据类型格式为:元素数据类型...[元素个数],取决于元素的数据类型与元素的个数:两个数组的元素的数据类型不同,数组的数据类型不同;两个数组的元素的数据类型相同,但元素个数不同,那么数组的数据类型也不同。所以数组的数据类型是千变万化的。
例如:

int arr[10] = {0};

数组arr的数据类型为:int...[10]

数组指针变量是指一个指针变量指向的对象是一个数组。在之前就提过,&数组名得到的是一个指向数组的指针,所指向对象的量级是整个数组,数组指针变量就是用于存放指向数组的指针。由于数组的数据类型是千变万化的,那么数组指针变量的数据类型也是千变万化的。

数组指针变量的定义格式如下:

int arr[10] = {0};
int (*parr) [10] = &arr;

以上代码中,int ... [10]就是数组的数据类型,代表指针所指向的对象,*parr结合代表parr是指针变量。

之所以要加上()是因为[]的运算符优先级高于*,如若不加会导致parr[]先结合:

int* parr[10] = &arr;

意思就变成了定义一个整型指针数组。

数组指针变量一般用于在二维数组传参中担任形参的角色。


2.8、数组指针变量与二维数组的关系

我们知道一个二维数组arr[m][n],arr内置的是指向首个一阶元素的指针,而arr[m]内置的是指向第m+1个一阶元素内部的首个二阶元素的指针。

例如:

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

其中,arr内置的指针指向首个一阶元素:一维数组{0,1,2,3}
arr[0]内置的指针指向第1个一阶元素(一维数组{0,1,2,3})的首个二阶元素:0
arr[1]内置的指针指向第2个一阶元素(一维数组{4,5,6,7})的首个二阶元素:4
arr[2]内置的指针指向第3个一阶元素(一维数组{8,9,10,11})的首个二阶元素:8

由此可看出,二维数组的数组名内置的指针是指向一维数组的数组指针。

其实原因就是:
arr[0] 实际上就是*(arr+0)arr+0的结果依然是指向{0,1,2,3}的数组指针,而*会将数组指针弱化为指向数组首个一阶元素的指针,故而*(arr+0)是指向二阶元素0的整型指针。

arr[1] 实际上就是*(arr+1)arr+1的结果依然是指向{4,5,6,7}的数组指针,而*会将数组指针弱化为指向数组首个一阶元素的指针,故而*(arr+1)是指向二阶元素0的整型指针。

arr[2] 实际上就是*(arr+2)arr+2的结果依然是指向{8,9,10,11}的数组指针,而*会将数组指针弱化为指向数组首个一阶元素的指针,故而*(arr+2)是指向二阶元素0的整型指针。

同时也可以印证:在创建二维数组时,第一个[]内的数字不能省略,因为二维数组中arr[n]的格式是一个有意义的整体,即指针。


2.9、数组指针的弱化

十分重要:
&数组变量名得到的是指向数组的指针,再对这个指针进行解引用:*(&数组变量名),会得到指向首个一阶元素的指针,也就是数组变量名内置的指针。原因是因为解引用操作符*会将数组指针弱化为指向首个一阶元素的指针。
例如:

int arr[5] = {0};
int (*parr) [5] = &arr;
printf("%zd\n",sizeof(*parr));

以上代码中,*parr等价于arr

也可以这样理解:parr实质就是&arr,由于*&相遇会发生消解,故而*parr==*&arr==arr

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值