『C语言入门』C语言数组详解

文章目录

  • 引言
  • 一、一维数组
    • 1.1 数组的创建
    • 1.2 数组的初始化
    • 1.3 一维数组的使用
    • 1.4 一维数组在内存中的存储
      • 1.4.1 内存中的数组布局
      • 1.4.2 计算元素的内存地址
      • 1.4.3 内存中的数组可视化
      • 1.4.4 指针与数组的关系
      • 1.5.5 内存布局的影响
  • 二、二维数组
    • 2.1 二维数组的创建
    • 2.2 二维数组的初始化
    • 2.3 二维数组的使用
    • 2.4 二维数组在内存中的存储
  • 三、数组越界
    • 3.1 数组越界的问题和风险
    • 3.2 避免数组越界
  • 四、数组作为函数参数
    • 4.1 数组名是指向首元素的指针
    • 4.2 数组名在函数参数中的特性
    • 4.3 避免数组越界
    • 4.4 数组名与指针的差异
  • 五、总结
    • 5.1 一维数组的重要性和使用
    • 5.2 二维数组的构建和使用
    • 5.3 避免数组越界问题
    • 5.4 数组作为函数参数的特性
    • 5.3 避免数组越界问题
    • 5.4 数组作为函数参数的特性

引言

数组在C语言编程中扮演着重要角色,它们让我们能够高效地存储和管理数据。本文将带您探索一维数组,学习如何创建、初始化和使用数组,以及它们在计算机内存中的存储方式。让我们深入了解C语言中这个基础而强大的概念。

一、一维数组

1.1 数组的创建

在C语言中,数组是一种用于存储同类型数据元素的线性数据结构。当创建一个数组时,涉及到以下细节:

  • 类型和名称: 首先,你需要确定数组的数据类型(整数、浮点数、字符等)以及为数组命名,这将用于在代码中引用数组。
// 例如,创建一个能容纳5个整数的数组
int myArray[5];
  • 大小的声明: 通过方括号中的数字来定义数组的大小,这决定了数组可以容纳的元素数量。数组的大小在创建后通常是固定的。

1.2 数组的初始化

数组初始化是指在创建数组的同时为数组的每个元素赋予初始值。这里有一些更深入的方面需要考虑:

  • 显式初始化: 你可以在创建数组时使用花括号为数组的每个元素提供初始值。这些值按照你提供的顺序分配给数组元素。
// 创建并初始化一个整数数组
int myArray[5] = {1, 2, 3, 4, 5};
  • 部分初始化和默认值: 如果未为数组的所有元素提供初始值,剩余的元素将被自动设置为默认值,例如,整数数组中未初始化的元素将被设置为0。
// 前三个元素被初始化为1、2、3,剩余的元素自动设为0
int myArray[5] = {1, 2, 3};

1.3 一维数组的使用

了解如何使用数组中的元素以及如何遍历数组对于编程至关重要。这里有更多的细节要考虑:

  • 通过索引访问: 数组中的元素通过索引来访问,索引从0开始,表示数组中的第一个元素。
int thirdElement = myArray[2];  // 获取第三个元素(索引为2)
  • 遍历数组: 使用循环(如for循环)可以遍历数组中的所有元素,进行操作。
for (int i = 0; i < 5; i++) {printf("%d ", myArray[i]);  // 打印数组的所有元素
}

1.4 一维数组在内存中的存储

1.4.1 内存中的数组布局

在C语言中,一维数组的元素是按照顺序在内存中连续存储的。这种连续存储的布局有助于高效的内存访问,同时也决定了数组元素之间的关系。

  • 数据类型的大小: 在理解内存布局之前,需要知道数组元素的数据类型的大小,例如,int类型通常是4字节。

1.4.2 计算元素的内存地址

了解如何计算数组元素的内存地址对于深入理解内存布局至关重要。数组的内存地址可以通过数组的起始地址加上索引乘以每个元素的大小来计算。

// 假设myArray在内存中的起始地址是1000
// 每个整数占用4个字节
// 计算第三个元素(索引为2)的内存地址
int thirdElementAddress = &myArray[2];  // 1000 + 2 * 4 = 1008

1.4.3 内存中的数组可视化

想象一下,你有一个整数数组myArray,包含5个元素。在内存中,它可能像这样布局:

内存地址    元素值 (每个元素占4字节)
1000       [0]
1004       [1]
1008       [2]
1012       [3]
1016       [4]

从上面的布局中可以看出,第一个元素位于起始地址1000,而每个元素都相对于前一个元素的地址增加了4个字节。这就是数组连续存储的本质。

1.4.4 指针与数组的关系

指针在理解数组的内存存储中起着重要作用。数组名本身就是一个指向数组起始位置的指针,可以用于访问数组中的元素。

int *ptr = myArray;  // ptr指向数组的起始位置
int thirdElement = *(ptr + 2);  // 通过指针访问第三个元素的值

1.5.5 内存布局的影响

了解数组的内存布局对于编程至关重要,因为它影响了程序的性能。连续的内存布局允许CPU高效地预取和缓存数组元素,从而提高访问速度。

二、二维数组

2.1 二维数组的创建

二维数组是一种表格状的数据结构,可以将其视为行和列的组合。在C语言中,创建二维数组包括指定数组的类型和名称,以及行数和列数。

// 创建一个3行4列的整数类型的二维数组
int myArray[3][4];  // 声明一个名为myArray的3行4列整数二维数组

2.2 二维数组的初始化

初始化二维数组是在创建数组的同时为每个元素赋予初始值。这可能需要更多的关注,尤其是在初始化多维数组时。

// 创建并初始化一个3行4列的整数二维数组
int myArray[3][4] = {{1, 2, 3, 4},   // 第一行的元素{5, 6, 7, 8},   // 第二行的元素{9, 10, 11, 12} // 第三行的元素
};

2.3 二维数组的使用

使用二维数组涉及到通过行索引和列索引来访问元素。这些索引从0开始,表示数组中的第一个行或列。

int x = myArray[1][2];  // 获取第二行、第三列的元素值(行索引为1,列索引为2)

你可以使用嵌套循环来遍历整个二维数组,进行操作。

for (int i = 0; i < 3; i++) {for (int j = 0; j < 4; j++) {printf("%d ", myArray[i][j]);  // 打印整个二维数组的元素}printf("\n");  // 换行以区分行
}

2.4 二维数组在内存中的存储

深入了解二维数组在内存中的存储方式有助于更好地理解其索引和访问方式。

  • 连续存储: 在内存中,二维数组的元素实际上是以一维数组的形式连续存储的,每一行的元素排列在一起,行与行之间相邻。
  • 内存地址计算: 计算二维数组中元素的内存地址需要使用起始地址、行索引、列数以及每个元素的大小。
// 假设myArray在内存中的起始地址是2000
// 每个整数占用4个字节
// 计算第二行第三列(行索引为1,列索引为2)的内存地址
int elementAddress = &myArray[1][2];  // 2000 + 1 * 4 * 4 + 2 * 4 = 2024

这样的存储方式允许C语言模拟实现了二维数组的访问。

三、数组越界

3.1 数组越界的问题和风险

数组越界是指试图访问数组之外的元素,这可能会导致以下问题和风险:

  1. 未定义行为(Undefined Behavior): C语言标准中未定义了数组越界的行为。这意味着当你访问超出数组范围的元素时,编译器不会为此提供任何保证,程序可能会表现出无法预测的结果,包括崩溃、输出错误的值,甚至在不同情况下可能有不同的行为。
  2. 内存损坏: 越界访问可能会影响到数组元素以外的内存区域。这可能会导致内存损坏,导致数据的意外改变,影响到其他变量或程序的执行。
  3. 安全漏洞: 数组越界访问是缓冲区溢出等安全漏洞的常见原因之一。攻击者可能会通过越界访问来修改其他关键变量的值,执行恶意代码,甚至窃取敏感数据。

3.2 避免数组越界

为了避免数组越界,有一些实用的方法和最佳实践:

  1. 检查索引范围: 在访问数组元素之前,始终检查索引是否在合法的范围内。可以使用条件语句来验证索引的有效性。
if (index >= 0 && index < arrayLength) {// 执行数组元素访问操作
} else {// 处理越界情况,如报错或返回错误码
}
  1. 使用循环: 在使用循环遍历数组时,确保循环的索引在合法范围内。循环条件应考虑数组长度。
  2. 注意多维数组: 对于多维数组,确保每个维度的索引都在有效范围内。例如,对于 int myArray[3][4];,确保行索引在0到2之间,列索引在0到3之间。
  3. 使用sizeof 在使用数组时,可以使用 sizeof 运算符来获取数组的大小,以便进行索引的合法性检查。

四、数组作为函数参数

当数组作为函数参数传递时,数组名是一个指向数组首元素内存地址的常量指针。数组名实际上被解释为指向数组首元素的指针,这使得函数能够访问整个数组。

让我们更详细地解释:

4.1 数组名是指向首元素的指针

在C语言中,数组名是一个指向数组首元素的常量指针。这意味着数组名实际上是首元素的内存地址。当你将数组作为函数参数传递时,函数会接收数组名,并将其视为指向数组首元素的指针。

考虑以下代码:

void printArray(int arr[], int size) {// 在这里,arr 是一个指向数组首元素的指针// 它与 &arr[0] 是等价的for (int i = 0; i < size; i++) {printf("%d ", arr[i]);  // 通过指针运算访问数组元素}
}int main() {int myArray[5] = {1, 2, 3, 4, 5};printArray(myArray, 5);  // 传递数组名和数组大小return 0;
}

在上述代码中,当我们传递 myArrayprintArray 函数时,arr 在函数内部实际上被视为指向 myArray 首元素的指针。因此,通过 arr[i] 的方式可以访问数组元素。

4.2 数组名在函数参数中的特性

当数组名作为函数参数传递时,有一些关键特性需要注意:

  • 数组名会退化为指向首元素的指针。在函数参数声明中,int arr[] 实际上被理解为 int *arr,因此 arr 是一个指针。
  • 由于传递的是指针,函数内部无法直接获取传递的数组大小。通常需要将数组大小作为额外的参数传递给函数,以避免越界访问。

4.3 避免数组越界

在函数参数中传递数组时,必须特别注意数组越界问题。由于函数无法获取传递数组的确切大小,因此在函数内部应使用传递的数组大小来避免越界访问。

4.4 数组名与指针的差异

尽管数组名在函数参数中退化为指针,但数组名和指针之间仍然存在一些差异:

  • 数组名是常量指针,一旦指向某个内存地址,就无法再指向其他地方。而指针变量可以重新赋值为其他内存地址。
  • 数组名可以使用 sizeof 运算符来获取整个数组的大小,而指针只能获取指针本身的大小。

五、总结

通过本文的探讨,我们深入了解了在C语言中创建、初始化和使用一维数组的过程,同时还探讨了二维数组的相关内容。此外,我们还研究了数组越界问题以及如何将数组作为函数参数传递。以下是我们从这篇文章中所获得的关键信息:

5.1 一维数组的重要性和使用

  • 一维数组是C语言中的重要数据结构,用于有效地存储和管理一系列相同类型的数据。
  • 创建数组时,我们需要指定数据类型、数组名称和大小。
  • 数组可以通过显式初始化来设置初始值,也可以部分初始化,未初始化的元素会被设为默认值。
  • 数组中的元素可以通过索引进行访问,索引从0开始,通过循环遍历数组可以实现对每个元素的操作。
  • 数组在内存中是连续存储的,可以通过内存地址计算和指针来访问元素。

5.2 二维数组的构建和使用

  • 二维数组是表格状的数据结构,适用于需要表示行和列关系的情况。
  • 创建二维数组时,除了数据类型和名称,还需要确定行数和列数。
  • 通过提供初始化值来初始化二维数组,每个元素通过行索引和列索引定位。
  • 二维数组的访问也是通过双重索引实现的,行和列索引都从0开始。

5.3 避免数组越界问题

  • 数组越界指试图访问数组范围之外的元素,可能导致未定义行为、内存损坏和安全漏洞。
  • 可以通过检查索引范围、使用循环时确保索引在有效范围内,以及使用sizeof运算符来避免数组越界问题。

5.4 数组作为函数参数的特性

  • 数组名作为函数参数时,实际上是传递了一个指向数组首元素的常量指针。
  • 在函数参数中,数组名退化为指针,无法直接获取数组大小,需要通过额外参数传递。
  • 二维数组的访问也是通过双重索引实现的,行和列索引都从0开始。

5.3 避免数组越界问题

  • 数组越界指试图访问数组范围之外的元素,可能导致未定义行为、内存损坏和安全漏洞。
  • 可以通过检查索引范围、使用循环时确保索引在有效范围内,以及使用sizeof运算符来避免数组越界问题。

5.4 数组作为函数参数的特性

  • 数组名作为函数参数时,实际上是传递了一个指向数组首元素的常量指针。
  • 在函数参数中,数组名退化为指针,无法直接获取数组大小,需要通过额外参数传递。
  • 了解数组名在函数参数中的特性有助于正确处理数组的大小和越界问题。

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

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

相关文章

JDK JRE JVM 三者之间的详解

JDK : Java Development Kit JRE: Java Runtime Environment JVM : JAVA Virtual Machine JDK : Java Development Kit JDK : Java Development Kit【 Java开发者工具】&#xff0c;可以从上图可以看出&#xff0c;JDK包含JRE&#xff1b;java自己的一些开发工具中&#…

第十五天|104.二叉树的最大深度、111.二叉树的最小深度、 222.完全二叉树的节点个数

104.二叉树的最大深度 题目链接&#xff1a;104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullp…

Java后端开发面试题——消息中间篇

RabbitMQ-如何保证消息不丢失 交换机持久化&#xff1a; Bean public DirectExchange simpleExchange(){// 三个参数&#xff1a;交换机名称、是否持久化、当没有queue与其绑定时是否自动删除 return new DirectExchange("simple.direct", true, false); }队列持久化…

[Linux]环境变量

[Linux]环境变量 文章目录 [Linux]环境变量环境变量的概念查看环境变量环境变量的加载原理环境变量的添加刷新环境变量配置文件的路径 环境变量的概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。 环境变量的本质&#xff1a;…

css层叠关系

文章目录 cascading声明冲突应用重置样式表a元素的类选择器顺序问题 cascading cascading – 层叠 解决声明冲突的过程&#xff0c;浏览器会自动处理&#xff1b;就是计算样式的权重&#xff0c;权重大的就被选择&#xff1b; 声明冲突 是指多个选择器选中同一个标签&#x…

AMEYA360代理品牌:纳芯微芯片解决方案为光伏市场赋能

近年来&#xff0c;光伏市场进入了一个新的增长维度。SolarPower Europe数据显示&#xff0c;2022年全球光伏新增装机量达239GW&#xff0c;占所有可再生能源新增容量的三分之二。国家能源局也宣称&#xff0c;2022年我国工商业光伏新增装机达25.87GW&#xff0c;同比增长236.7…

C#面向对象程序设计之变量的作用域,深入浅出 入门和进阶教程3

1、效果镇楼: 最近忒忙了!真的忙到不可开交的呢,繁杂业务的处理真的不是您,我个人想象的样子,完全比您个人想象的要复杂至少三倍以上的难度!也是客观事实。 菜鸟程序员面临的客观残酷现实!尤其您这个年龄阶段,实在是堪忧,尴尬的很啊,非常严峻的形势,也可以说特别严峻…

Element table根据字段合并表格(可多字段合并),附带拖拽列动态合并

效果如图&#xff0c;姓名 数值1 字段进行自动合并 封装合并列js - tableMerge.js // 获取列合并的行数 // params // tableData: 表格数据 // mergeId: 合并的列的字段名 export const tagRowSpan (tableData, mergeId) >{const tagArr [];let pos 0;tableData.map((i…

vue组件的scope 以及 如何样式穿透

个人复习&#xff01;&#xff01;&#xff01; 有什么用 让当前组件的样式不会修改到其它地方的样式&#xff0c;使用了data-v-hash的方式来使css有了它对应模块的标识 实现原理 1、给HTML的dom节点添加一个不重复的data属性(例如: data-v-5558831a)来唯一标识这个dom 元素…

linux启动jar 缺失lib

linux启动jar包时&#xff0c;找不到报错 [rootebs-141185 xl-admin]# java -Djava.library.path/home/kabangke/xl-admin/lib -jar /home/kabangke/xl-admin/xl-admin.jar Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/web/se…

诺依框架ruoyi.js添加默认当年日期范围

ruoyi.js添加方法 // 默认当年日期范围如&#xff1a;2023-01-01到2023-08-22&#xff08;至今&#xff09; export function defaultYearDate(data) {// this.dateDefaultShow new Date();// this.dateDefaultShow.setMonth(new Date().getMonth() - 1);const end new Date…

ubuntu 对多CPU统一设置高性能模式

文章目录 一、问题描述二、软件安装与设置三、查看各CPU状态四、开机默认高性能4.1 安装cpufrequtils4.2 编写脚本4.3 设为默认开机脚本 参考链接 一、问题描述 之前在网上找到的CPU设置高性能模式&#xff0c;只能设置CPU0单个CPU&#xff0c;下述是对多核CPU统一设置工作模式…

Linux 设置mysql开机自启动和安装JDK

0目录 1.mysql设置开机自启动 2.linux安装jdk 1.mysql设置开机自启动 去到cd /etc/rc.d/init.d目录 创建一个sh脚本 编辑脚本 设置开机自启动 重启 检查是否自启动 2.linux安装jdk 下载安装包 放在opt目录下 新建soft文件夹&#xff08;opt目录下&#xff09;…

【VS_C++基础知识】

很高兴在雪易的CSDN遇见你 &#xff0c;给你糖糖 欢迎大家加入雪易社区-CSDN社区云 前言 本文分享VS_C的基础知识&#xff0c;希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴的点赞关注&#xff0c;小易会继续努力分享&#xff0c;一起进步&#xff01; 你的点赞就…

leetcode做题笔记94. 二叉树的中序遍历

给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 思路一&#xff1a;模拟题意 void inorder(struct TreeNode*root,int* ans,int *resSize) {if(!root){return ;}inorder(root->left,ans,resSize);ans[(*resSize)] root->val;inorder(root->right…

“深入解析JVM内部原理:探索Java虚拟机的工作机制“

标题&#xff1a;深入解析JVM内部原理&#xff1a;探索Java虚拟机的工作机制 摘要&#xff1a; Java虚拟机&#xff08;JVM&#xff09;是Java语言的核心组成部分&#xff0c;它负责将Java源代码编译为可执行的字节码&#xff0c;并提供运行时环境。本文将深入探索JVM的工作机…

一套基于C#语言开发的LIMS实验室信息管理系统源码

实验室信息管理系统&#xff08;LIMS)是指帮助实验室组织和管理实验数据的计算机软件系统&#xff0c;它将实验室操作有机地组织在一起&#xff0c;以满足实验室工作流程的所有要求。它能以不同的方式支持实验室的工作&#xff0c;从简单的过程(如样品采集和入库)到复杂的流程(…

stm32的位带操作

在51单片机中&#xff0c;我们可以使用P2^1来对单片机的某一位进行操作&#xff0c;到了stm32&#xff0c;我们通过位带操作&#xff0c;将寄存器的每一位映射到一个32位的地址。如下是我查资料摘录的一些图片。 映射方式 SRAM: AliasAddr 0x22000000 (A-0X20000000)*8*4n*4…

vue 简单实验 v-if 条件判定

1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"conditional-rendering"><span v-if"seen">现在你看到我了</span> </div> <script> const C…

rust库学习-env_logger(actix-web添加彩色日志、rust添加彩色日志 )

文章目录 介绍actix-web启用彩色日志crate地址&json格式日志 我们在进行rust的web开发时&#xff0c;如果不指定日志&#xff0c;就不会有输出&#xff0c;非常不友好 这里我们使用env_logger进行日志打印 介绍 env_logger 需要配合 log 库使用, env_logger 是 Rust 社区…