深入探寻指针奥秘之数组与指针的爱恨情仇(上)

0. 前言

------家人们,答应你们的指针下一篇来啦,这期我们就来详细讲讲指针与数组之间的爱恨情仇!!!

一、数组名的理解

首先在深入探讨他们之间的关系前,我们先来重新认识一下数组这就老朋友。

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

看过我的深入探寻指针奥秘的老朋友们肯定对上面这段代码不陌生,新朋友也不要担心,可以点开看看,说不定会给你带来对指针的新理解。
那么我们今天重新来讲解一下这段代码,我们通过&arr[0]来获取数组第一位元素的地址,但是数组名本身就是首元素的地址,不信我们来做个测试。

int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("&arr[0] = %p\n", &arr[0]);
	printf("arr     = %p\n", arr);
	return 0;
}

【在这里插入图片描述
我们数组名和数组首元素的地址打印出来结果是一模一样的,由此不难得出 数组名就是数组首元素的地址,但是它是有特殊情况的!

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

输出结果为四十,如果是首元素地址的话,应该要输出4/8才对呀。没错这就是特殊情况中的一种。

  • sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
  • &数组名,这里的数组名同样也是表示整个数组,取出来的是整个数组的地址
    除了这两个特例以外,任何地方使用数组名,数组名都表示首元素的地址。
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("&arr[0] = %p\n", &arr[0]);
	printf("arr     = %p\n", arr);
	printf("&arr    = %p\n", &arr);
	return 0;
}

哈哈哈哈不要高兴的太早来看看这段代码呢。
在这里插入图片描述
这就很奇怪了,不是说&数组名取出的是整个数组的地址吗?!那这样的话arr又和&arr有啥区别呢?上代码!!!

int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("&arr[0]   = %p\n", &arr[0]);
	printf("&arr[0]+1 = %p\n", &arr[0] + 1);
	printf("arr       = %p\n", arr);
	printf("arr+1     = %p\n", arr + 1);
	printf("&arr      = %p\n", &arr);
	printf("&arr+1    = %p\n", &arr + 1);
	return 0;
}

在这里插入图片描述
这里我把编译器换成了x64位,看到这个结果是不是更迷惑了,这里我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1也是相差4个字节,但是&arr和&arr+1相差40个字节,这是因为&arr[0]和arr都是首元素地址,+1就是跳过一个元素。但是&arr是数组的地址,==+1是跳过整个数组。==现在对数组名的理解加深了很多吧。

二、指针访问数组

我们正式开始讨论指针和数组之间的联系。有了前面这些知识的铺垫,我们就能很好的来使用指针访问数组啦。

int main()
{
	int arr[10] = { 0 };
	int n = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	for (int i = 0; i < n; i++)
	{
		scanf("%d", p + i);//也可以写scanf("%d",arr+i);
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", p[i]);
	}
	return 0;
}

在代码中注释提及的地方,因为这里arr是首元素的地址可以赋值给p,所以这里的arr和p是等价的,那么我们可以使用arr[i]访问数组元素,p[i]同样也可以,将代码中的p[i]换成*(p+i)也是一样的效果,本质上这两个是等价的。
同理arr[i]也应该等价于*(arr+i),数组遍历在编译器处理时,也是转换成首元素的地址+偏移量求出元素地址, 然后解引用访问的。

三、一维数组的传参本质

我们之前都是在函数外部计算数组的元素个数,那我们可以把函数传给一个函数后,函数内部求数组的元素个数嘛?

void test(int arr[])
{
	int n2 = sizeof(arr) / sizeof(arr[0]);
	printf("n2 = %d\n", n2);
}
int main()
{
	int arr[10] = { 0 };
	int n1 = sizeof(arr) / sizeof(arr[0]);
	printf("n1 = %d\n", n1);
	test(arr);
	return 0;
}

在这里插入图片描述
我们发现函数内部没有正确的获取到数组的元素个数。在数组传参时,传递的是数组名,本质上数组传递的是数组首元素的地址,所以理论上应该使用指针变量来接收首元素地址。正是因为函数的参数部分本质是指针,所以函数内部是没办法求数组元数个数的。那么在函数内部我们用sizeof(arr)计算一个地址的大小,而不是数组的大小。

void test(int arr[])
{
	printf("arr[] = %d\n",sizeof(arr));
}
void test1(int* arr)
{
	printf("*arr = %d\n", sizeof(arr));//计算一个指针变量的大小
}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	test(arr);
	test1(arr);
	return 0;
}

一维数组传参,形参可以用数组形式,也可以是指针形式

四、二级指针

指针变量也是变量,是变量它就会有地址,那指针的地址又存放在什么地方呢?这就不得不说到二级指针
在这里插入图片描述

int main()
{
    int a = 10;
    int * pa = &a;
    int ** ppa = &pa;
    return 0; 

地址都是我假设的,看了这个图是不是稍微对下面这段代码好理解一点了。

  • *ppa: 对ppa进行解引用,找到的是&pa,其实就是访问pa。
  • *ppa: 先通过ppa找到pa,然后对pa解引用(*pa),找到a。

五、指针数组

小问答:请问指针数组是指针还是数组?
整型数组,是存放整型的数组,字符数组是存放字符的数组,那么。。。指针数组就是存放指针的数组啦!!!指针数组的每个元素是地址,又可以指向一块区域。
我们来用指针数组模拟一下二维数组呢?

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* parr[3] = { arr1,arr2,arr3 };
	int i, j;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

在这里插入图片描述
parr[i]是访问parr数组的元素,找到的数组元素指向整型一维数组,parr[i][j]就是整型一维数组中的元素。但是其实上述代码并不完全是二维数组,因为每一行并非连续。

本期的内容就暂时讲到这里啦,下一篇文章我们会进一步探讨指针,数组,函数等等之间的关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值