深入理解指针(2)

本篇文章主要围绕指针和数组的关系以及数组的理解来讲述。下面我们就来看看有关指针和数组有关的内容。

目录

1.数组名的理解

2.结合数组使用指针访问

2.1.数组使用指针访问

2.2.辨析指针和数组的区别 

3.一维数组传参的本质

4.二级指针

4.1.二级指针的申明

4.2.二级指针的运用 

5.指针数组

5.1.指针数组的申明

5.2.数组指针模拟二维数组


1.数组名的理解

上一章我们对采用&arr[0]的方式拿到了数组第一个元素的地址,但是其实数组名本身就是地址,并且是首元素的地址。

那我们不妨联想到三种取地址的方法如下面代码:

#include <stdio.h>
int main()
{
	int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	int* p1 = &arr[0];
	int* p2 = arr;
	int* p3 = &arr;
	return 0;
}

下面我们就讨论这三个的区别

如下图

我们可知 &arr[0] arr &arr 三个值相等,但是表示的含义不同。

  • arr 与 &arr[0] 两者含义基本相同都是表示数组首元素的地址,进行 + 1 操作后,地址跨越一个单位的类型大小
  • 而&arr 表示整个数组的地址,所以进行 + 1 操作后,地址跨越整个数组

但是有一个例外,当 sizeof(数组名) 如 sizeof(arr),这里数组名代表整个数组,计算整个数组的大小,如下面图示代码:

 sizeof(&arr) 与 sizeof(&arr[0]) 代表的是这个地址的大小,可以看做是一个指针存储该地址,这个指针的大小,而不是数组首元素的大小。

2.结合数组使用指针访问

2.1.数组使用指针访问

如下代码就是用指针简单地进行访问数组的操作。

#include <stdio.h>
int main()
{
	int arr[10] = {0};
	// 使用指针来访问数组
	int sz = sizeof(arr) / sizeof(arr[0]);	// 数组的长度
	int* p = arr;	// 指针p 指向数组arr
	// 输入10个数
	for (int i = 0; i < sz; i++)
	{
		// 输入i个值
		scanf("%d", p + i);	// p + i == &arr[i]	
	}
	// 输出10个数
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));	// *(p + i) == arr[i]
	}
	return 0;
}

上面代码中指针p 可以完全被数组名arr 所替换,因为数组名arr本身就是首元素的地址。

所以 arr[i] === *(arr + i)

其实编译器在编译arr[i] 时自动将其转换成右边的形式

如果我们多多尝试还可以发现 arr[i] == *(arr + i) == *(i + arr) == i[arr]

大家可能对i[arr] 有点诧异,下面做一个简单的解释:

[ ] 其实就是一个双目操作符,其实下标引用操作符,i[arr]编译器会将其转换成*(i + arr)

2.2.辨析指针和数组的区别 

虽然上面说指针p 可以直接代还成数组名arr ,但是指针和数组还是有本质上的区别的。

  • 数组就是数组,是一块连续的空间(数组的大小和数组元素个数和元素类型都有关系)
  • 指针(变量)就是指针(变量),是一个变量,其大小是4/8个字节(有操作系统的位数有关)。

3.一维数组传参的本质

样例一:

#include <stdio.h>
void Print(int arr[10])
{
	int sz = sizeof(arr) / sizeof(arr[0]);    // 计算数组的大小
	for (int i = 0; i < sz; i++)    // 遍历输出
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	Print(arr);    // 传入数组名arr(本质上是一个地址)
	return 0;
}

执行结果:

1

这时就有疑问产生了,为什么输出结果不是1~10,只是1呢?

原因:

数组传参的时候,形参可以写成数组的形式,但是本质上还是指针变量。所以变量sz 在计算数组的长度时,sizeof(arr)只是一个指针变量的大小,并不是整个数组的大小。

数组传参的本质是传递了数组首元素的地址,所以形参访问的数组和实参的数组是同一个数组。

形参的数组是不会单独再创建数组空间的。所以形参的数组可以省略数组大小。


样例二:

#include <stdio.h>
void Print(int* p, int sz)	// 用指针p 来接收地址
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
}
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	int sz = sizeof(arr) / sizeof(arr[0]);	// 计算数组的大小
	Print(arr, sz);		// arr 是数组首元素的地址
	return 0;
}

执行结果为:

1 2 3 4 5 6 7 8 9 10

这就可以正常的输出整个数组的数值了,只需要将数组长度在函数外面计算完成后传入函数中就行了。

总结:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。 但是传入的参数是地址,所以不能在函数内计算数组的大小。

4.二级指针

变量有地址,指针变量也是变量,所以也有地址,指针变量的地址存放在一个指向指针变量的指针变量,这句话可能有点绕口,其实就是二级指针,这个指针变量存放了另一个指针的地址。

4.1.二级指针的申明

那么二级指针变量如何申明呢?

上章我们学到了指针申明就是变量类型 + ' * ' + 变量名

那么二级指针也是如此

int* *p;        代表指针p指向一个int* 类型的变量,即指针p是一个二级指针

如下面代码就是二级指针的申明以及初始化的方式 

int a = 10;
int* pa = &a;    // 指向a的一个一级指针变量
int** ppa = &pa;  // 指向指针p的一个二级指针变量
4.2.二级指针的运用 

由图可知二级指针和一级指针时一样的,通过解引用可以查询地址对应的数据,由此我们可以延伸到多级指针的运用,但是一般在实际编写代码的时候,我们一般会用到二级指针,但是再高级的一般用不上。

5.指针数组

在学习指针数组之前,我们得先明确指针数组是一个数组,而不是指针,指针是作为修饰主语数组的,其含义就是存放指针的数组。

就像int a[10] 是一个整型数组,存放的是整型数据,char s[10]是一个字符型数组,存放的是字符型数据。

5.1.指针数组的申明

指针数组的申明也是一样申明类型 + 变量名

int* a[10];        这样就是简单的申明了一个指针数组,注意区分指针数组和数组指针

因为[ ] 优先级比 * 高,所以变量名先和[ ]组合成数组,这个数组的类型是int *

5.2.数组指针模拟二维数组

上章我们说过arr[i] = *(arr + i)        所以p[i][j] = *(*(p + i) + j)

故可以用指针数组模拟二维数组的实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值