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;
}

运行结果:

图解:

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


相关推荐
安全系统学习8 分钟前
网络安全之内核初级对抗技术分析
开发语言·python·算法·安全·web安全
晴空闲雲1 小时前
数据结构与算法-线性表-线性表的应用
数据结构
CPETW2 小时前
同旺科技 USB TO SPI / I2C适配器(专业版)--EEPROM读写——C
c语言·开发语言·科技·stm32·单片机·嵌入式硬件·电子
@解忧杂货铺2 小时前
基于用户的协同过滤推荐算法实现(Java电商平台)
算法·机器学习·推荐算法
呆呆的小鳄鱼2 小时前
IO之详解cin(c++IO关键理解)
linux·c语言·c++
爱coding的橙子2 小时前
每日算法刷题Day31 6.14:leetcode二分答案2道题,结束二分答案,开始枚举技巧,用时1h10min
算法·leetcode·职场和发展
@ chen2 小时前
龟兔赛跑算法(Floyd‘s Cycle-Finding Algorithm)寻找重复数
算法
晨曦学习日记2 小时前
leetcode题解538:把二叉搜索树转换为累加树
算法
孟大本事要学习2 小时前
算法第13天|继续学习二叉树:平衡二叉树(递归)、二叉树所有路径(递归)、左叶子之和(递归)
学习·算法
wangjialelele2 小时前
双向链表——(有头双向循环链表)
数据结构·链表