当前位置: 首页 > news >正文

单片机/C/C++八股:(二十二)数组名,以及和指针的区别(一/二维数组)

上一篇下一篇
include <> 和 include “” 的区别


数组名,以及和指针的区别(一/二维数组)

常见误区:

  1. 数据名是第一个元素的首地址,是个常量
  2. 数据名是一个指针(是个常量指针)
  3. 数据名当函数参数的时候,传递的是指针
  4. 数组名相当于指针
  5. 数组名有特殊含义,是特殊指针
  6. 数组名不可以是左值
  7. &arrarr地址相同,所以类型一样

这些描述和解释都不准确,为什么?

1)数组名详解

一个一维数组的类型是:类型[长度],一个二维数组的类型是:类型[行数][列数]不是简单的字符型、整型、浮点型……

而数组名是该数组对象的标识符(代表一块连续内存的标识符,可以理解成常量地址),其本身并不属于任何类型:

  • 在一些表达式中使用数组名时,它的类型通常被视为该数组的类型。
  • 但在大多数情况下,数组名会退化为指向首元素的指针,类型变为指针(例如int*)。

1.1)一维数组

① 声明与类型:

intarr[5]={1,2,3,4,5};
  • arr是一个包含 5 个int的数组。数组的类型是int[5]arr作为标识符,在不退化时代表整个数组。

② 关键行为:

表达式类型含义说明
arrint*(退化后)指向数组第一个元素arr[0]的指针,值=第一个元素的地址
&arrint (*)[5]指向整个数组的指针,值=数组首地址(注意:不是int**
sizeof(arr)size_t返回5 * sizeof(int),因为未退化
arr + 1int*指向arr[1],步长为sizeof(int)
&arr + 1int (*)[5]地址增加5 * sizeof(int),跳过整个数组

⚠️这里尤其要区分arr&arr,虽然他们的值都是数组的起始地址(第一个元素的地址就是数组起始地址),但是两者的类型和含义完全不同,所以会导致指针算数行为完全不同(参考上述表格的后两行)。

示例:

printf("%p\n", (void*)arr); // 如 0x1000 printf("%p\n", (void*)(arr + 1)); // 0x1004 (+4 字节) printf("%p\n", (void*)&arr); // 0x1000 (与 arr 相同) printf("%p\n", (void*)(&arr + 1)); // 0x1014 (+20 字节)

③ 函数传参(退化发生):

voidfunc(inta[]){/* 等价于 int* a */}// 或voidfunc(int*a);
  • 此时a是指针,sizeof(a)返回指针大小(如 8),无法得知原数组长度
  • 必须额外传递长度:func(arr, 5);

1.2)二维数组

① 声明与类型:

intmat[3][4];// 3 行,每行 4 个 int
  • 数组的类型是int[3][4]。它是一个一维数组的数组:外层数组有 3 个元素,每个元素是int[4]类型。

  • 二维数组的内存分布为以arr[2][5]为例:

② 数组名的含义:

表达式类型含义
matint (*)[4](退化后)指向第 0 行(即mat[0],类型为int[4])的指针
&matint (*)[3][4]指向整个二维数组的指针
sizeof(mat)3*4*sizeof(int)整个二维数组大小(未退化)
sizeof(mat[0])4*sizeof(int)第一行的大小(mat[0]int[4]
mat + 1int (*)[4]指向第 1 行(地址 + 16 字节)
&mat + 1int (*)[3][4]地址 + 整个数组大小(48 字节)

示例:

printf("%p\n", (void*)mat); // 如 0x2000 printf("%p\n", (void*)(mat + 1)); // 0x2010 (+16 字节 = 4*int) printf("%p\n", (void*)&mat); // 0x2000 printf("%p\n", (void*)(&mat + 1)); // 0x2030 (+48 字节)

③ 元素访问:

  • mat[i][j]等价于*(*(mat + i) + j)
  • mat[i]是第i行,类型为int[4],在表达式中退化为int*

④ 函数传参(二维数组):

必须指定列数(因为编译器需知道每行宽度以计算偏移):

voidfunc(intm[][4],introws);// √ 推荐voidfunc(int(*m)[4],introws);// √ 等价写法(m 是指向 int[4] 的指针)voidfunc(int**m);// × 错误!不能接收二维数组(除非是动态分配的指针数组)

💡 原因:mat退化为int (*)[4],不是int**int**表示“指针的指针”,而二维数组是“连续内存块”。

1.3)数组名什么时候能取地址?

表达式是否左值?能否取地址?
arr✔️ 是(数组对象)✔️&arrint (*)[3][4]
arr[0]✔️ 是(子数组对象)✔️&arr[0]int (*)[4]
arr[0][0]✔️ 是(int 对象)✔️&arr[0][0]int*
arr + 1❌ 否(右值)❌ 非法
arr[0] + 1❌ 否(右值)❌ 非法
&arr[1]✔️ 是(左值)✔️ 类型int (*)[4]

关键:只有代表内存中实际对象的表达式(左值)才能取地址。指针算术的结果(如p + n)是右值,不能取地址。

正确等价关系(二维数组) :

想表达的含义正确写法值(地址)
第 0 行首地址arr&arr[0]&arr[0][0]
第 1 行首地址arr + 1&arr[1]&arr[1][0]
第 0 行第 1 列地址arr[0] + 1&arr[0][1]&arr[0][1]
整个数组地址&arrarr,但类型不同

2)数组名与指针的区别

类型:

  • 数组名:是一个常量地址,代表整个数组的首地址
  • 指针:是一个变量,存储某个地址值

可修改性:

  • 数组名:不能被赋值或修改(如arr = p;非法)
  • 指针:可以被重新赋值(如p = arr; p++;合法)

地址与值的含义

  • 对数组名取地址(&arr):
    • 类型是int (*)[5](指向整个数组的指针)
    • &arr + 1会跳过整个数组(5 个 int)
  • 对指针取地址(&p):
    • 类型是int **
    • p + 1跳过一个元素(1 个 int)

3)什么时候数组名会/不会退化为(常量)指针

在这些情况下,才能勉强说数组名是个常量指针

3.1)不会退化

数组名不退化的三大场景(C 标准规定),无论一维还是多维,以下情况都不会触发数组到指针的退化:

表达式(场景)是否退化结果
sizeof(数组名)→ \rightarrow作为sizeof的操作数整个数组所占字节数
&数组名→ \rightarrow作为一元&(取地址)的操作数指向数组的指针
char s[] = "hello"→ \rightarrow用于初始化字符数组的字符串字面量

其中第一种场景:在编译期间,数组名的类型会被编译器当作类型[长度],即代表整个数组,而sizeof函数是编译时运算符,sizeof(数组名)等价于sizeof(类型[长度])=长度*sizeof(类型)

3.2)会退化

大多数场景下,数组名会退化为指向首元素的指针,类型变为指针(例如int*):

表达式(场景)是否退化结果
void func(int arr[]){}→ \rightarrow作为函数参数传递时✔️此时arr是指针,sizeof(arr)返回指针大小(如 8)
a✔️指针
a + 1✔️指针

4)案例

#include"stdio.h"intmain(){inta[2][5]={1,2,3,4,5,6,7,8,9,10}int*ptr1=(int*)(&a+1);int*ptr2=(int*)(*(a+1));printf("%d,%d",*(ptr1-1),*(ptr2-1));return0;}

运行结果为:

10,5

http://www.jsqmd.com/news/523211/

相关文章:

  • 传输矩阵法仿真:解决偏振态反射谱、镜片镀膜设计与光纤传输矩阵的广泛应用
  • 2023最新图像隐写实战:5个GitHub热门项目代码实测与性能对比
  • 2026年林欣电子有限公司氖灯:中小制造企业的稳定光源解决方案 - 博客湾
  • Mujoco 物体pickup总失败?摩擦力有哪些(切向、扭转、滚动)
  • MiniCPM-o-4.5-nvidia-FlagOS实战:为Claude等AI助手构建本地知识库增强系统
  • 关于类和对象的基本区别
  • sql盲注 sqli-lab8
  • 整理2026年广州无版纸箱印刷机排名,无版纸箱印刷机精品定制推荐 - 工业设备
  • cv_resnet50_face-reconstruction多场景落地:支持千万级证件库的批量人脸标准化重建调度系统设计
  • K8S 高级调度-叩丁狼
  • 2025-2026年中国精益生产咨询公司推荐:工厂现场改善口碑机构及用户反馈分析 - 品牌推荐
  • AI智能体视觉检测(TVA)与常规AI视觉检测的区别
  • 「权威评测」2026年国内虫情测报灯厂家实力推荐,谁才是靠谱之选? - 深度智识库
  • CasRel模型在Ubuntu服务器上的生产环境部署详解
  • 剖析2026年昆山高效分选机排名,高性价比厂家推荐 - mypinpai
  • 2026年虫情测报灯厂家推荐:从“经验判断”到“数据说话”的智慧选择 - 深度智识库
  • 最好用的文档解密大师——文档密码恢复大师
  • Flyway、Liquibase难以覆盖 NineData 的多环境发版流程编排能力?
  • 2026年中国精益生产咨询公司推荐:中小企业降本增效靠谱选择与口碑评价 - 品牌推荐
  • 2026年会计学论文降AI率工具推荐:财经类同学用这几款最顺手
  • Cartographer建图后,如何用两种方法正确保存.pgm地图文件(附避坑点)
  • 深度剖析:OpenClaw Skill 的生命周期与执行引擎
  • FLUX.1-dev适合谁?给想体验顶级画质又怕配置麻烦的AI绘画新手
  • 2026年高新技术企业认定公司推荐:科技企业资质升级全流程服务与高通过率机构盘点 - 品牌推荐
  • 遥感新手别迷茫!PIE Engine Studio保姆级入门指南(从注册到第一个NDVI分析)
  • 怎么用 API 搭一个 AI 客服机器人?从 0 到上线的完整踩坑记录
  • 2026年3月上海瀛新园电话:公墓、墓地、墓园、传统中式墓、生态葬选择指南 - 海棠依旧大
  • 基于MCU与MPS数字电源模块的可编程电源控制系统
  • STLink工具从v1.7.0到v1.8.0版本升级全指南
  • 深度解析APP侵害用户权益的十大典型问题及合规整改指南