【C语言初阶(12)】数组(超详解)

文章目录

  • 前言
  • 1. 一维数组
    • 1.1 一维数组的创建
    • 1.2 一维数组的初始化
    • 1.3 一维数组的访问
    • 1.4 一维数组在内存中的存储
  • 2. 二维数组
    • 2.1 二位数组的创建
    • 2.2 二维数组的初始化
    • 2.3 二维数组的访问
  • 3. 数组越界
  • 4. 数组作为函数参数
    • 4.1 冒泡排序介绍
    • 4.2 冒泡排序函数的设计
  • 5. 数组名
  • 6. 数组应用实例

前言

  • 有时候可能需要保存大量类型一致的数据,如一个班级里边所有学生的成绩,手机通讯录中所有联系人的电话,斐波那契数列的前 100 数……对于这些类型一致、数量庞大的数据,如果使用不同变量来存储,就会让人觉得变成是一件很痛苦的事情。
  • 例如,班级中有 50 名学生,那么总共就需要创建 50 个整形变量来存放他们的成绩,如果很不幸,恰好这次又是期末考试,总共考了 5 个科目,那么每一科要创建 50 个变量,总共就需要创建 250 个变量,然后再依次赋值。
#include <stdio.h>
int main()
{int a1, a2, a3, a4, a5, ..., a50;int b1, b2, b3, b4, b5, ...,b50;int c1, c2, c3, c4, c5, ..., c50;int d1, d2, d3, d4, d5, ..., d50;int e1, e2, e3, e4, e5, ..., e50;......scanf("%d", &a1);......scamf("%d", &a50);scanf("%d", &b1);......return 0;
}
  • 应该不会有人会写出这种鬼代码出来,所以,C 语言就引入了数组的概念。

1. 一维数组

1.1 一维数组的创建

  • 数组就是存储一批同类型数据的地方,定义一维数组的语法格式为:类型 数组名[常量表达式];
int a[6];//定义一个整形数组,总共存放 6 个元素,每个元素都是一个整型
char b[24];//定义一个字符型数组,总共存放 24 个元素,每个元素为一个字符
double c[3];//定义一个双精度浮点型数组,总共存放 3 个元素,每个元素为一个双精度浮点型
  • 在定义数组时,需要在数组名后面紧跟一对方括号,其中的数量用来指定数组中元素的个数。

数组的大小

  • 在 C99 标准之前,数组的大小必须是常来那个或者常量表达式;
  • 在 C99 标准之后,数组的大小可以是变量,为了支持变长数组。

1.2 一维数组的初始化

  • 在创建数组的同时对其各个元素进行赋值,称为数组的初始化。
  • 数组初始化得方式有很多,主要分为完全初始化不完全初始化两大类。

完全初始化

  1. 对数组中得每个元素都进行赋值。
int arr[10] = {1,2,3,4,5,6,7,8,9,10};

不完全初始化

  • 只对部分元素进行赋值,其余未被赋值的元素自动初始化为 0。
int arr[10] = {1,2,3};//对前 3 个元素进行赋值,其余 7 个元素系统自动初始化为 0

1.3 一维数组的访问

  • 对于数组的使用之前介绍了一个操作符:[ ] ,下标引用操作符。它其实就是数组访问的操作符。
  • 访问数组的的语法格式为:数组名[下标]
a[0];//访问 a 数组中的第一个元素
b[1];//访问 b 数组中的第二个元素
c[5];//访问 c 数组中的第三个元素
  • 注意:在数组中,下标为 0 的才是第一个元素

计算数组中的元素个数

  • 并不是所有的时候都能知道数组的大小,此时就需要让编译器自己去算数组中有多少个元素。
int sz = sizeof(数组名)/sizeof(数组名[首元素下标]);
//计算数组大小,并将结果返回给 sz
  • 数组最后一个元素的 下标就是 sz - 1。

访问数组中的每个元素

  • 既然已经知道了数组是通过下标来访问的,并且也知道了如何求数组大小,那么自然也能知道如何访问数组当中的每个元素。

在这里插入图片描述

总结

  1. 数组是使用下标来访问的,下标是从0开始。
  2. 数组的大小可以通过计算得到。
int arr[10];
int sz = sizeof(arr)/sizeof(arr[0]);

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

先说结论

  • 数组在内存中是连续存放的

在这里插入图片描述

举个栗子

在这里插入图片描述

  • 每个元素之间的地址都差了 4 个字节

2. 二维数组

  • 随着开发的深入,在有些情况下,一维数组难以满足开发的要求,引入二维数组的概念之后,问题就变得简单很多了。
  • 二维数组通常也被称之为矩阵,将二维数组写成行和列的形式表示

2.1 二位数组的创建

  • 定义二维数组的方法和一维数组类似,语法格式为:类型 数组名[常量][常量] ;
int a[5][5];//5*5,5行5列
char b[4][5];//4*5,4行5列
double c[6][3];//6*3,6行3列

在这里插入图片描述

  • 二维数组行列的下标都是从 0 开始的

  • 注意:这里需要强调的是几行几列我们是从概念模型上来看的,也就是说,这样来看待二维数组,我们可以更容易理解。但从物理模型上看,无论是二维数组还是更多维的数组,在内存中仍然是以线性的方式存储的。

  • 比如,定义了二维数组 int b[4][5];那么 b 在内存中的存放就如下图所示。

在这里插入图片描述

  • 从图中可以看出,二维数组事实上就是在一维数组的基础上,每个元素存放一个数组。同样道理,三维数组,四维数组都是以同样的方式实现。
  • 可以把二维数组理解为,一个一维数组,这个一维数组的每个元素都是一个一维数组。

2.2 二维数组的初始化

二维数组的初始化方式和一维数组类似,下面介绍二维数组的六种初始化方式。

  1. 二维数组在内存中是线性存放的,因此可以将所有的数据写在一个大括号内。
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
//先将第一行的四个元素初始化,再初始化第二行的元素
  1. 为了更加直观地表示元素的分布,可以用大括号将每一行的元素括起来,会更加清晰。
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
  1. 二维数组也可以只对部分元素赋值,这样写是只对各行的第一列元素赋值,其余元素全部初始化为 0。
int a[3][4] = {{1},{5},{9}};
  1. 如果希望整个二维数组初始化为 0,那么直接在大括号里写一个 0 即可。
int a[3][4] = {0};
  1. C99 同样增加了一种新特性:指定初始化的元素。这样可以只对数组中得某些指定元素进行初始化赋值,而未被赋值的元素自动被初始化为 0。
int a[3][4] = {[0][0] = 1,[1][1] = 2,[2][2] = 3};
  1. 二维数组的初始化也能 “ 偷懒 ”,让编译器根据元素的数量计算数组的长度,但是只有第一维的元素个数可以不写,其他维度必须写上(可以省略行,不能省略列)。
int a[][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12};

2.3 二维数组的访问

  • 访问二维数组中得元素,同样是使用方括号 [ ]。语法格式为 数组名[下标][下标] ;
a[0][0];//访问 a 数组中第一行第一列的元素
b[1][3];//访问 b 数组中第二行第四列的元素
c[3][3];//访问 c 数组中第四行第四列的元素

举个栗子

  • 将二维数组的所有元素打印到屏幕上。

在这里插入图片描述

3. 数组越界

  • 在使用数组时,要防止数组下标超出边界。也就是说,必须确保下标是有效的值。
int arr[10];
  • 在使用该数组时,要确保程序中使用的数组下标在 0~9 的范围内;
  • 因为编译器不会检查出这种错误(但是,一些编译器会发出警告,然后继续编译程序)。

越界访问数组

  • 使用越界的数组下标会导致程序改变其他变量的值。

在这里插入图片描述

预防数组越界

  • 可以使用 sizeof 来让程序自己判断数组的大小,从而避免自己犯迷糊了导致数组越界。

在这里插入图片描述

4. 数组作为函数参数

  • 往往我们在写代码的时候,会将数组作为参数传个函数;
  • 比如:我们要实现一个冒泡排序函数将一个整形数组排序。

4.1 冒泡排序介绍

基本思想

  • 将两两相邻的元素进行比较,小的放左边,大的放右边(交换数据),按照递增来排序,不满足条件就交换数据,满足则不管。
  • 将某个数排到最终的位置,这一轮叫一趟冒泡排序

在这里插入图片描述

  • 每一趟冒泡排序可以让 1 个元素走到最终位置.
    • 对于 6 个元素要进行 5 趟冒泡排序。
    • n 个元素要进行 n - 1 趟冒泡排序

在这里插入图片描述

冒泡排序过程(升序)

初始:21,25,49,25*,16,08 ;n = 6。

  1. 第 1 趟
    • 第 1 趟结束后:21,25,25*,16,08,49
    • 第 1 趟结束之后,49 已经有序了,那么下一趟就不用管它了。

在这里插入图片描述

  1. 第 2 趟
    • 第 2 趟结束后:21,25,16,08,25*,49
    • 继续下一趟,每一趟增加一个有序元素。

在这里插入图片描述

  1. 第 3 趟结果:21,16,08,25,25*,49
  2. 第 4 趟结果:16,08,21,25,25*,49
  3. 第 5 趟结果:08,16,21,25,25*,49

冒泡排序算法总结

  • n 个元素,总共需要 n - 1 趟冒泡排序
  • 第 i 趟需要比较 n - i - 1 次

4.2 冒泡排序函数的设计

1. 冒泡排序函数的错误设计

  • 错误的使用了数组名传参,在函数体内部利用数组名求数组元素个数。
//冒泡排序函数的错误设计
#include <stdio.h>
void bubble_sort(int arr[])//arr看似是数组,实则为指针
{int n = sizeof(arr) / sizeof(arr[0]);//数组名传参传过来的实际是个指针,32 位系统下指针为 4 个字节;//所以 n 的结果为 4 / 4 = 1,元素个数都算不对,下面的代码更不用说了//算出 n = 1 的话,n-1=0,循环压根就没进去,自然不会排序了for (int i = 0; i < n - 1; i++){for (int j = 0; j < n - 1 - i; j++){int tmp = 0;if (arr[j] > arr[j + 1])//交换数据{tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
int main()
{int i = 0;int arr[] = { 10,9,8,7,6,5,4,3,2,1 };bubble_sort(arr);//传过去的是数组首元素的地址for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}return 0;
}

在这里插入图片描述

2. 冒泡排序函数的正确设计

  • 在函数体外部求出数组元素个数,然后将元素个数传参给函数。
//冒泡排序函数的正确设计
#include <stdio.h>
void bubble_sort(int arr[], int n)
{for (int i = 0; i < n-1; i++)//n 个元素,总共需要进行 n-1 趟冒泡排序{for (int j = 0; j < n - 1 - i; j++)//每一趟冒泡排序要比较 n-i-1 次{int tmp = 0;if (arr[j] > arr[j + 1])//交换数据{tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
int main()
{int i = 0;int arr[] = { 10,9,8,7,6,5,4,3,2,1 };int n = sizeof(arr) / sizeof(arr[0]);//使用数组名传参的话,如果要用到数组元素个数,必须在主函数中求出结果再传给函数bubble_sort(arr,n);//数组名传参传过去的是数组首元素的地址for (i = 0; i <n; i++){printf("%d ", arr[i]);}return 0;
}

在这里插入图片描述

5. 数组名

绝大多数情况下,数组名是数组首元素的地址,但有两种情况例外。

  1. sizeof(数组名):这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名:取出的是整个数组的地址。
#include <stdio.h>
int main()
{int arr[10] = 0;printf("%p\n", arr);//arr 就是首元素的地址printf("%p\n", arr + 1);//地址+1,跳过一个元素printf("---------------------\n");printf("%p\n", &arr[0]);// 取出首元素的地址printf("%p\n", &arr[0] + 1);//跳过一个元素printf("---------------------\n");printf("%p\n", &arr);//整个数组的地址的表现形式也是首元素地址printf("%p\n", &arr + 1);//但是数组地址+1会跳过整个数组return 0;
}

在这里插入图片描述

二维数组的数组名的理解

  • 二维数组的数组也表示首元素的地址,但二维数组的首元素和我们想的有点不太一样。
  • 二维数组的数组名表示的是第一行的地址
  • 对数组名+1会直接跳过一行。

在这里插入图片描述

二维数组行列数的计算

  1. 计算行数:总数组的大小 / 一行的大小
sizeof(数组名) / sizeof(数组名[0]);
//二维数组第一行的数组名是 数组名[0]
  1. 计算列数:一行的大小 / 一个元素的大小
sizeof(数组名[0]) / sizeof(数组名[0][0]);

在这里插入图片描述

6. 数组应用实例

  1. 三子棋游戏
  2. 扫雷游戏

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

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

相关文章

PSI算法经典论文算法概述

文章目录 什么是隐私求交PSIPSI协议分类PSI算法的分类基于哈希函数的PSI算法基于不经意传输&#xff08;OT&#xff09;的 PSI算法基于GC的PSI算法基于公钥加密的PSI算法基于DH的PSI算法基于RSA盲签名的PSI算法基于同态加密的PSI算法 基于差分隐私的PSI算法 总结参考文献 什么是…

利用Python构建宁德时代、比亚迪、隆基绿能股票时间序列预测模型

存货 import tushare as ts # 导包 import numpy as np import matplotlib.pyplot as plt from scipy.signal import find_peaks from scipy.stats import norm import datetime import pandas as pd import seaborn as sns # pip install seaborn import matplotlib.patches …

数据在计算机中的存储——【C语言】

在前面的博客中&#xff0c;我们已经学习了C语言的数据类型&#xff0c;先让我们回顾一下C语言中有哪些数据类型。 目录 C语言的基本内置类型 类型的基本归类 整型在内存中的存储 原码、反码、补码 存储中的大小端 练习 浮点型在内存中的存储 浮点数的存储规则 对引例问…

css基础知识十八:CSS如何画一个三角形?原理是什么?

一、前言 在前端开发的时候&#xff0c;我们有时候会需要用到一个三角形的形状&#xff0c;比如地址选择或者播放器里面播放按钮 通常情况下&#xff0c;我们会使用图片或者svg去完成三角形效果图&#xff0c;但如果单纯使用css如何完成一个三角形呢&#xff1f; 实现过程似乎…

三、Java的运算符

三、运算符 运算符是一种特殊的符号&#xff0c;用以表示数据的运算、赋值和比较等。 3.1、算术运算符 - * / % (前) (后) (前)-- (后)-- % :取余运算 结果的符号与被模数的符号相同 开发中&#xff0c;经常使用%来判断能否被除尽的情况 3.2、赋值运算符 注意&#xff1a;s2…

哈工大计算机网络课程网络层协议详解之:Internet路由BGP协议详解

哈工大计算机网络课程网络层协议详解之&#xff1a;BGP协议详解 在之前的网络层协议中&#xff0c;我们介绍了Internet网络两个自治系统内的路由协议&#xff1a;RIP协议和OSPF协议。这两个协议应该来说是自治系统内协议的两个代表性协议&#xff0c;前一个基于距离向量路由算…

LabVIEW开发空气动力学实验室的采集和控制系统

LabVIEW开发空气动力学实验室的采集和控制系统 在航空航天模拟设施中&#xff0c;通常的做法是准备一种针对当前正在进行的实验的数据采集和控制软件。该软件通常是根据当前要求编辑的更通用程序的修订版&#xff0c;或者可能是专门为该测试编写的自定义程序&#xff0c;具体取…

mysql重点复习

1.MySQL如何对用户smart授权访问&#xff0c;密码为123456。 2.授权用户tom可以在网络中的192.168.4.254主机登录&#xff0c;仅对对userdb库下的user表有查看记录、更新name字段的权限 &#xff0c; 登录密码userweb888。 GRANT SELECT,UPDATE(name) ON userdb.user TO tom192…

基于matlab使用两个图像估计校准相机的姿势(附源码)

一、前言 运动结构 &#xff08;SfM&#xff09; 是从一组 3-D 图像估计场景的 2-D 结构的过程。此示例演示如何从两个图像估计校准相机的姿势&#xff0c;将场景的三维结构重建为未知比例因子&#xff0c;然后通过检测已知大小的对象来恢复实际比例因子。 此示例演示如何从使…

网络管理(包括网络管理的功能,管理系统及相关协议:SNMP,CMIS/CMIP)

1.OSI定义的网络管理 OSI定义的网络管理功能有以下5大类 (1)性能管理(PerformanceManagement) 在最少的网络资源和最小时延的前提下&#xff0c;网络能提供可靠、连续的通信能力。性能管理的功能有性能检测、性能分析、性能管理、性能控制。 (2)配置管理(ConfigurationManagem…

【Docker】Docker安装MySQL

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前专攻C/C、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL、蓝桥杯&am…

K个一组反转链表

K个一组反转链表 题目&#xff1a;力扣25 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有…