目录

什么是数组(Array)?

🔍为什么数组的下标要从 0 开始? 

一、内存地址与偏移量的关系:从 0 开始是最自然的映射

二、指针的起点就是第 0 个元素的地址

三、历史原因:BCPL → B → C → C++

数组的内存体现

 数组的声明

数组的访问方式 


什么是数组(Array)?

数组(Array)是 C++ 中的一种线性数据结构,用于存储多个相同类型的变量,并且这些变量在内存中是连续排列的。

你可以把它想象成一个排好队的储物柜,每个柜子有编号(下标),每个柜子里放着一个值。例如:

int arr[5] = {10, 20, 30, 40, 50};

这表示我们声明了一个包含 5 个 int 类型的数组,它依次存储:

  • arr[0] = 10

  • arr[1] = 20

  • arr[2] = 30

  • arr[3] = 40

  • arr[4] = 50

注意:数组的下标从 0 开始,而不是 1。


🔍为什么数组的下标要从 0 开始? 

虽然最初很多人觉得「从 1 开始」更符合直觉,但数组从 0 开始其实是有深刻的底层原因和效率考量,它与 指针、地址计算、语言设计哲学 有关。我们来系统解释这个设计逻辑。 

一、内存地址与偏移量的关系:从 0 开始是最自然的映射

在 C/C++ 中,数组实际上是指针加偏移(pointer arithmetic)。

例子:

int A[4] = {10, 20, 30, 40};

假设数组 A 从地址 0x1000 开始,且每个 int 占 4 字节。

下标 i内存地址数学计算
A[0]0x1000A + 00x1000 + 0 * 4
A[1]0x1004A + 10x1000 + 1 * 4
A[2]0x1008A + 20x1000 + 2 * 4

 访问 A[i] 实际上是计算:

*(A + i)  // 指针 + 偏移量

 👉 如果下标从 1 开始,那就必须写成:

*(A + (i - 1))

 这样会多一个运算(减法),无论在运行效率还是语义上都不自然。

二、指针的起点就是第 0 个元素的地址

当你声明:int A[5];

数组名 A 实际上是指向 A[0] 的地址。不是 A[1],不是别的起点。

所以,

*A     == A[0]
*(A+1) == A[1]
*(A+2) == A[2]

 如果下标从 1 开始,就会出现“偏移一格”的矛盾,代码会更难维护。

三、历史原因:BCPL → B → C → C++

🧬 C语言起源于 B 和 BCPL

最早的语言 BCPL 和 B语言 中没有数组的概念,只有“地址 + 偏移”。C 语言继承了这种偏移访问模型,所以自然地,数组从 0 开始偏移。

Dennis Ritchie(C 语言的设计者)就是遵循这个简洁、底层直观的设计哲学。

现代语言很多也从 0 开始

大多数现代语言也继承了这个设计:

语言数组是否从 0 开始
C✅ 是
C++✅ 是
Java✅ 是
Python✅ 是
JavaScript✅ 是
Rust✅ 是

虽然也有一些语言(如 Fortran、Lua)允许你从 1 开始索引,但这并不常见。


数组的内存体现

数组的核心特征是:所有元素在内存中是挨着排放的,没有任何间隔。

我们用一个直观的内存图解来说明:

假设 int 类型占用 4 字节(常见情况),数组如下:

int arr[4] = {100, 200, 300, 400};

 如果 arr[0] 存储在内存地址 0x1000,那么在内存中是这样的:

内存地址       值
0x1000      arr[0] = 100
0x1004      arr[1] = 200
0x1008      arr[2] = 300
0x100C      arr[3] = 400

▶️ 特点总结:

  • 每个元素都紧挨着上一个,偏移量是 sizeof(类型)

  • 编译器知道数组是连续的,所以可以通过起始地址和偏移快速定位任意元素:
    arr[i] 等价于 *(arr + i)


 数组的声明

在 C++ 中,声明数组就是告诉编译器我们要创建一个连续内存区域,用于存储多个相同类型的数据项。声明时必须指定类型和元素数量。 

1. int A[5];

含义:

  • 声明一个整型数组 A,包含 5 个元素。

  • 未初始化,每个元素的值是未定义的垃圾值(在局部变量中)。

注意:

  • 在函数内部声明的数组(局部数组)不会自动清零。

  • 在全局或静态作用域中声明的数组会被自动初始化为 0。

2. int A[5] = {2, 4, 6, 8, 10};

 含义:

  • 声明一个大小为 5 的整型数组,并完全初始化所有元素。

  • A[0] = 2, A[1] = 4, ..., A[4] = 10

特点:

  • 初始化列表刚好填满数组,无自动补零。

  • 所有元素值由你控制。

3. int A[5] = {2, 4};

含义:

  • 声明一个大小为 5 的数组,只初始化前两个元素。

  • 剩下的元素会被自动补零。

 结果是:

A[0] = 2  
A[1] = 4  
A[2] = 0  
A[3] = 0  
A[4] = 0

4. int A[5] = {0};

含义:

  • 声明一个大小为 5 的数组,仅第一个元素初始化为 0。

  • 其余元素也会被自动补零。

  • 快速清零的技巧:用 {0} 初始化整个数组。

实际效果:

A[0] = 0  
A[1] = 0  
A[2] = 0  
A[3] = 0  
A[4] = 0

5. int A[] = {2, 4, 6, 8, 10};

含义:

  • 不指定大小,由初始化列表的元素数量自动推断大小为 5。

  • 效果与 int A[5] = {2, 4, 6, 8, 10}; 相同。

编译器推断出:int A[5];     // ← 实际等价形式

 更简洁,特别是在你明确初始化所有元素的情况下。

 


数组的访问方式 

通过索引访问数组元素(Index)

这是最常见、最直接的方式。

A[index]
  • index 是整数类型,从 0 开始。

  • 索引值必须在合法范围内:0数组大小 - 1

用指针访问数组元素 

数组名与指针的关系:

在大多数表达式中,数组名会自动退化为指向第一个元素的指针:

int A[5] = {1, 2, 3, 4, 5};
int* p = A;         // A 就是 &A[0]

此时:

  • pA 都指向数组开头

  • 你可以用指针访问元素

✅ 使用 *(pointer + index): 

int A[5] = {1, 2, 3, 4, 5};
int* p = A;cout << *(p + 0);   // 输出 1
cout << *(p + 3);   // 输出 4*(p + 2) = 100;     // 修改 A[2] 为 100

🟰 等价关系:

表达式含义
A[i]访问数组第 i 个元素
*(A + i)使用数组名当指针
*(p + i)使用指针访问数组元素
p[i]指针变量也支持 [] 下标运算(语法糖)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/87126.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/87126.shtml
英文地址,请注明出处:http://en.pswp.cn/web/87126.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频内存太大怎么压缩变小一点?视频压缩的常用方法

视频传生活或者工作中很常见&#xff0c;如发送视频邮件、在线视频播放、视频上传下载等。未压缩的大内存视频文件传输时&#xff0c;不仅会消耗大量的网络带宽资源&#xff0c;还会使传输时间大幅增加。在网速有限的情况下&#xff0c;发送一个几 GB 的未压缩视频可能需要数小…

性能测试包括哪些方面?要掌握哪些知识

性能测试是软件测试中的一个重要方面&#xff0c;它主要关注软件在不同条件下的稳定性、可靠性和性能表现。性能测试包括多个方面&#xff0c;需要掌握的知识也相对广泛。以下是对性能测试包括的方面以及需要掌握的知识分析&#xff1a; 一、性能测试包括的方面 响应时间&…

windows的vscode无法通过ssh连接ubuntu的解决办法

现象&#xff1a; 最近在windows本地通过vscode登录ssh时发现不得劲&#xff0c;总是报错无法与”192.168.1.129“建立连接&#xff0c;如下图&#xff1a; 但是这种报错以及在输出端的信息并没有提供具体错误原因&#xff0c;于是换poweshell来登录&#xff0c;报错如下图&am…

第2章,[标签 Win32] :Windows 的字符串函数

专栏导航 上一篇&#xff1a;第2章 &#xff1a;兼容 ASCII 字符与宽字符的 Windows 函数调用 回到目录 下一篇&#xff1a;无 本节前言 在下面的文章链接里面&#xff0c;我们谈到过&#xff0c;使用兼容版的字符串处理函数的知识。 第2章 &#xff1a;编写兼容多字节字…

Java的SpringAI+Deepseek大模型实战-会话记忆【三】

文章目录 背景项目环境实现步骤第一步、定义会话存储方式方式一、定义记忆存储ChatMemory方式二、注入记忆存储ChatMemory 第二步、配置会话记忆方式一、老版本实现方式二、新版本实现 第三步、添加会话ID 异常处理1、InMemoryChatMemory 无法解析 背景 前两期搭建起大模型对话…

Python3完全新手小白的学习手册 10 文件和异常

文章目录 读取文件读取文件的全部内容 相对路径和绝对路径访问文件中的各行使用文件的内容包含100万位的大型文件圆周率值中包含你的生日吗&#xff1f; 写入文件写入一行写入多行 异常处理ZeroDivisionError异常使用try-except代码块else代码块处理FileNotFoundError异常分析文…

VC Spyglass:工具简介

相关阅读 VC Spyglasshttps://blog.csdn.net/weixin_45791458/category_12828932.html?spm1001.2014.3001.5482 传统上&#xff0c;基于仿真的动态验证技术一直是功能验证的核心方式。随着现代SoC设计日益复杂&#xff0c;静态验证技术的引入变得愈发重要。 Synopsys的 VC Sp…

AWS RDS Aurora全局数据库转区域数据库实战指南:无缝迁移零停机

Aurora全局数据库是AWS提供的跨区域高可用解决方案,但在某些场景下,我们需要将其转换为普通区域数据库。本文将详细介绍这一转换过程,并分享关键技术要点和实战经验。 一、全局数据库与区域数据库概述 AWS RDS Aurora全局数据库是一种跨区域部署的数据库架构,主要用于灾备…

C++之路:函数重载与运算符重载

目录 函数重载运算符重载C运算符重载范围对照表注意事项 运算符重载语法全局运算符重载类内运算符重载下面以一个一元运算符为例&#xff0c;介绍特性1&#xff1a;下面介绍特性3&#xff1a;&#xff08;必须类内重载的运算符‌&#xff09; 函数重载 函数重载是指同一个作用域…

七、SpringCloud 项目迁移至 K8s

七、SpringCloud 项目迁移至 K8s 文章目录 七、SpringCloud 项目迁移至 K8s1、环境准备1.1 集群规划1.2 SpringCloud 项目架构及迁移需求分析 2、迁移 Eureka 集群2.1 构建及容器化2.2 部署至 K8s2.3 创建通信Service 3、迁移网关服务3.1 构建及容器化3.2 部署至 K8s3.3 创建Se…

通过具有一致性嵌入的大语言模型实现端到端乳腺癌放射治疗计划制定|文献速递-最新论文分享

Title 题目 End-to-end breast cancer radiotherapy planning via LMMs with consistency embedding 通过具有一致性嵌入的大语言模型实现端到端乳腺癌放射治疗计划制定 01 文献速递介绍 近年来&#xff0c;受大型语言模型&#xff08;LLM&#xff09;启发的新一代人工智…

MCP Chart Server服务本地部署案例

一、MCP Chart Server介绍 MCP Chart Server是一个专业的图表生成服务&#xff0c;支持多种图表类型&#xff0c;适用于数据可视化和分析。 MCP Chart Server是一种用于生成和呈现图表的服务器端软件。它提供了一个简单而强大的方式&#xff0c;让开发人员和系统管理员可以轻…

复合型浪涌保护器五大核心技术重构电气防护体系

开篇&#xff1a;从传统防护到智能守护的技术跨越 在电气设备面临浪涌威胁的防护场景中&#xff0c;浪涌保护器&#xff08;SPD&#xff09;始终扮演着关键角色。面对传统SPD在漏电流、续流等方面的技术局限&#xff0c;行业领先企业通过技术整合开发出复合型SPD&#xff0c;以…

c# 详细分析Task.sleep和Thread.sleep 的区别、使用场景和应用示例

文章目录 Task.Delay vs Thread.Sleep 详细分析与使用场景核心区别详细分析Thread.SleepTask.Delay 性能考量综合示例高级用法组合延迟与超时实现指数退避重试 总结建议 Task.Delay vs Thread.Sleep 详细分析与使用场景 核心区别 Task.Delay 和 Thread.Sleep 都用于在代码中引…

Vue 3 中的 `h` 函数详解

h 函数是 Vue 3 中用于创建**虚拟 DOM 节点(VNode)**的核心函数&#xff0c;它是 Vue 渲染系统的基石。下面我将全面解释它的作用、用法和重要性。 1. h 函数的基本概念 h 是 createVNode 的简称&#xff0c;来源于"hyperscript"的缩写传统。它的主要作用是&#x…

SoapCore 全面介绍:在 .NET Core 中实现 SOAP 服务的现代解决方案

一、什么是 SoapCore&#xff1f; 在现代微服务和 REST API 成为主流的今天&#xff0c;SOAP&#xff08;Simple Object Access Protocol&#xff09;看似已经被边缘化&#xff0c;但在许多企业和政务系统中&#xff0c;SOAP 仍然是 重要的通信协议。特别是在金融、保险、医疗…

JDBC工具类和SQL 注入问题

在软件开发中&#xff0c;数据库安全与高效访问一直是关键课题。本文将围绕 SQL 注入问题的原理、解决方案&#xff0c;以及 JDBC 开发中的工具类演进和连接池技术展开探讨&#xff0c;结合实际代码示例&#xff0c;为开发者提供清晰的技术实践指南。 SQL 注入问题的核心原理与…

2022年SEVC SCI2区,分数阶蚁群算法FACA:一种基于分数阶长期记忆的合作学习方法,深度解析+性能实测

目录 1.摘要2.分数阶微积分基础知识3.分数阶蚁群算法FACA4.分数阶蚁群算法FACA数学证明与分析5.结果展示6.参考文献7.代码获取8.算法辅导应用定制读者交流 1.摘要 本文提出了一种新颖分数阶蚁群算法&#xff08;Fractional-Order Ant Colony Algorithm&#xff0c; FACA&#…

java+vue+SpringBoo数字科技风险报告管理系统(程序+数据库+报告+部署教程+答辩指导)

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿ppt部署教程代码讲解代码时间修改工具 技术实现 开发语言&#xff1a;后端&#xff1a;Java 前端&#xff1a;vue框架&#xff1a;springboot数据库&#xff1a;mysql 开发工具 JDK版本&#xff1a;JDK1.…

YOLOv12_ultralytics-8.3.145_2025_5_27部分代码阅读笔记-augment.py

augment.py ultralytics\data\augment.py 目录 augment.py 1.所需的库和模块 2.class BaseTransform: 3.class Compose: 4.class BaseMixTransform: 5.class CutMix(BaseMixTransform): 6.class CopyPaste(BaseMixTransform): 7.def v8_transforms(dataset, img…