C语言指针进阶:数组与指针的联系

目录

  • [1. 数组名的本质](#1. 数组名的本质)
  • [2. 使用指针访问数组](#2. 使用指针访问数组)
  • [3. 一维数组传参的本质](#3. 一维数组传参的本质)
  • [4. 二级指针](#4. 二级指针)
  • [5. 指针数组](#5. 指针数组)
    • [5.1 指针数组模拟二维数组](#5.1 指针数组模拟二维数组)

正文开始。

1. 数组名的本质

数组名代表着这个数组中第一个元素的地址

例如:

c 复制代码
int arr[4] = { 1,2,3,4 };
int *p1 = &arr[0];
int *p2 = arr;

上述代码中指针变量p1p2所存储的地址是相同的。

数组名代表着数组中元素首元素的地址,但有两个特例需要特别关注:

  • sizeof(arr_name):sizeof 中单独放数组名,这里的数组名代表着整个数组,计算的是整个数组的大小,单位是字节。
  • &arr_name:这里的数组名表示的整个数组,取出的是整个数组的地址

除此之外,在其他任何地方使用数组名,它都表示首元素的地址。

需要注意的是,整个数组的地址和首元素的地址是有所不同的。整个数组的地址所指向的对象是一个自定义的数组类型 ;而首元素的地址所指向的对象是首元素的类型

例如:

可以看到,上述代码中,&arr所指向的对象类型为int [5],大小为20个字节;而arr所指向的对象类型为int,大小为4字节。

2. 使用指针访问数组

理解了数组名的本质,那我们就可以通过指针来访问数组了。

c 复制代码
#include <stdio.h>
int main()
{
	int arr[5] = { 0 };
	int size = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	//p == arr == &arr[0]
	for(int i = 0; i < size; i++)
	{
		scanf("%d", p + i);
	//p + 1 == arr + i == &arr[0] + i == arr[i] == p[i]
	}
	for(int i = 0; i < size; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

运行结果:

3. 一维数组传参的本质

我们时常需要将数组作为参数传递给函数,下面来讨论下一维数组传参的本质。

c 复制代码
#include <stdio.h>
void Num(int arr[10])
{
	int size = sizeof(arr);
	printf("%d", size);
}

int main()
{
	int arr[10] = { 0 };
	Num(arr);
	return 0;
}

32位环境下运行结果:

64位环境下运行结果:

在上述代码第11行调用函数时,函数的参数为arr,这里代表着该数组首元素的地址,所以传递进函数的参数实际上是一个地址

在函数定义时,用到了void Num(int arr[10]),其中的[10]只是一个形式上的声明,可以理解为告诉开发人员这是一个存放了十个元素的数组。

既然传入的参数本质上是一个地址,那我们当然也可以在定义函数的时候,就将参数设置为指针,例如上文代码等同于:

c 复制代码
#include <stdio.h>
void Num(int *p)
{
	int size = sizeof(p);
	printf("%d", size);
}

int main()
{
	int arr[10] = { 0 };
	Num(arr);
	return 0;
}

4. 二级指针

我们都知道,指针变量就是用来存放所指向对象的地址,那指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里呢?答案就是二级指针

c 复制代码
#include <stdio.h>
int main()
{
	int a = 5;
	//一级指针,存放变量 a 的地址
	int* p = &a;
	//二级指针,存放变量 p 的地址
	int** pp = &p;
	
	//指针的运算
	//一级指针解引用,*p == a == 5
	int b = *p;
	//二级指针解引用,*pp == p,**pp == a == 5
	int* c = *pp;
	int d = **pp;
	return 0;
}

这里只对二级指针做演示,按照二级指针的定义,以此类推,就可以得出多级指针的用法,这里不再赘述。

5. 指针数组

指针数组,究竟是指针呢,还是数组呢?

我们类比一下,韭菜盒子,到底是韭菜呢,还是盒子呢,很显然,它指的是韭菜馅的盒子。

那指针数组呢?是存放指针的数组

图例:

5.1 指针数组模拟二维数组

c 复制代码
#include <stdio.h>
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	//指针数组,每个元素都是一个指向一维数组的地址
	int* parr[3] = { arr1,arr2,arr3 };
	int i = 0;
	int j = 0;
	for(i = 0; i < 3; i++)
	{
		for(j = 0;j < 5; j++)
		{
			printf("%d ",parr[i][j]);
			//可理解为(parr[i])[j]
			//例如parr[1][3]
			//parr[1][3] == (parr[1])[3] == arr2[3] == 5
		}
		printf("\n");
	}
	return 0;
}

运行结果:

图解:

上述代码实现了二维数组的效果,但其本质上并不是二维数组,因为每一行在内存中并非是连续存储的,而二维数组的元素在内存中是连续存储的。


相关推荐
无限码力2 小时前
8.26华为OD机试真题 新系统【字符串回文判断】
算法·华为od·华为od机考·华为od机试·华为od上机考试真题·华为od最新机试真题题解·华为od机试真题题库
玖玥拾4 小时前
LeetCode 125 验证回文串
算法·leetcode
xu_wenming8 小时前
嵌入式软件架构中的6种解耦艺术
c语言·驱动开发·嵌入式硬件·架构
Asize10 小时前
146. LRU 缓存
算法
Asize11 小时前
543. 二叉树的直径
算法
lemon_sjdk11 小时前
ObjectProperty
java·开发语言·算法
Zentceh11 小时前
AI-ISP在夜视机芯中的应用:从传统ISP到PixelClean全彩夜视的进化
人工智能·科技·算法·计算机视觉·车载系统·视频·智能硬件
xier_ran11 小时前
【infra之路】AWQ 详解:激活感知权重保护,让 W4A16 量化精度超越 GPTQ
线性代数·算法·机器学习·量化·infra
亦皓ai12 小时前
AI时代后端工程(二):AI把代码写得越来越快,我却越来越不敢让它直接开工了
人工智能·算法·机器学习·搜索引擎·transformer
DevHub12 小时前
CANopen 教程:STM32 实现 CANopen 从站,3 个回调替代手写对象字典
c语言·驱动开发·stm32·单片机·嵌入式硬件·mcu