数组探秘:编程世界的万能容器

微软技术分享 微软全球最有价值专家
全栈领域优质创作者
博客专家认证
2025-02-04 20:39:19

在C语言中,数组是一种非常重要的数据结构,它允许我们存储固定大小的同类型元素集合。通过使用数组,我们可以方便地处理大量数据,提高程序的效率和可读性。数组的类型分为一位数组和二维数组,还有多维数组,而今天小编将要给大家介绍的是一维数组的相关知识。一起来看看吧!!!

https://i-blog.csdnimg.cn/direct/fbf845076eeb449599a3dd33530e0344.jpeg

那接下来就让我们开始遨游在知识的海洋!

正文

因为今天我们的任务就是学会一维数组,那自然首先我们要学习的就是它的概念——一维数组的概念

一 一维数组的概念

基本概念:一维数组(One-Dimensional Array)是计算机编程中最基本的数据结构之一,用于存储一系列相同类型的数据元素。这些元素在内存中按顺序连续存放,每个元素可以通过一个唯一的索引(Index)来访问。在C语言中这个索引是从0开始的整数。

相信大家在刚看到这些文字的时候,肯定有很多诸如此类的疑惑:什么是数据结构?什么叫元素可以通过唯一索引访问?什么是数组的索引?不要急,除了数据结构,小编在后文会为大家一一解惑。现在大家只要大概懂一维数组是用来存储一系列相同类型的数据元素的概念就行。

至于为什么说除了数据结构,这是因为数据结构并不是我们C语言初学者应该先了解的内容,我们现在应该学好C语言的基本语法,在后面小编会带着大家一起学习这一内容。

那接下来就跟着小编来学习一维数组的创建

二 一维数组的创建

首先我们来了解一维数组的格式

格式为:元素类型 数组名[元素个数]

就像这样:

         int  arr[10];
         char   arr[10];
         float  arr[10];
         bool   arr[10];

这样我们就创建了四个元素个数均为10个的整形数组,字符数组,浮点型数组,布尔型数组。

事实上,数组的类型还有很多,因为目前我们并没有学完C语言所有的数据类型,但也不用怕,等到了后面,再来学习。

学完创建,我们再来看看如何对一维数组进行初始化

三 一维数组的初始化:

事实上,数组的初始化有着部分初始化完全初始化之分,我们依次来学习。

(1)一维数组的完全初始化

顾名思义,把所有的数组元素赋值为我们想要的数值

例如:

    int arr[10]={1,2,3,4,5,6,7,8,9,10};

这样我们就对数组进行了完全初始化,每个数组元素就被我们从左往右按照数组下标从0到9进行了赋值,而这个下标就是前文——数组概念内提到的索引。至于为什么下标从零开始,这是C语言的规定。

我们还没有学习遍历数组,但这可以通过VS的调试窗口(有兴趣也可向小编了解)看到:

https://i-blog.csdnimg.cn/direct/30147aafa489413eb8a9533995af889d.png

接着再来看看一维数组的部分初始化

(2)一维数组的部分初始化

顾名思义,把部分(前面的)的数组元素赋值为我们想要的数值

例如:

int arr[10] = {1, 2};

这样我们就让数组的第一个元素和第二个元素赋值成了1,2,那剩下没进行赋初值的数组元素的值是多少呢?

事实上,编译器会自动把他们赋值为0

同理,这也可以通过VS的调试窗口(有兴趣也可向小编了解)看到:

https://i-blog.csdnimg.cn/direct/34b5a0eb985a4c3baeb163eed78e99e4.png

有图有真相,小编不是在乱说哦!

接下来,我们再来看看一维数组的类型是如何确定的。大家可以先思考一个问题:数组的类型一样,是不是说明是同一个数组?

四 一维数组类型:

类型格式为:元素类型 [元素个数]

例如:

int[10];

事实上,数组去掉数组名剩下来的就是数组的类型这也适用于更高维的数组

  • 由此可见类型一样,数组未必是同一个数组

学到这,我们对一维数组有了基本的认识,接下来就要学一学该如何使用一维数组。

五 一维数组的使用

数组的使用毫无疑问要从数组的下标开始讲起

数组的下标(索引)

  • 我们在这必须承认的规则:数组的下标是从0开始的

这一点从上面的调试窗口截图也可以看出。

也就是:

int[10]={1,2,3,4,5,6,7,8,9,10};
         0,1,2,3,4,5,6,7,8,9
 arr[0]==1,arr[9]==10;

那知道了这个,我们可以用在哪些地方呢?

接下来,就看看数组下标的运用场景:

1.遍历数组

使用循环(for循环或while循环)和数组下标来依次访问 数组中的每个元素。这是处理数组数据最常用的操作之一。

例如:

#include<stdio.h>
int main(){
int arr[10] = {1, 2, 3, 4, 5, 6, 7 ,8, 9, 10};
for(int i=0;i<10;i++)
{
   printf("%d ",arr[i]);
}
printf("\n");
}

运行结果为:

https://i-blog.csdnimg.cn/direct/43787948c5654ab2866ca8999a7cefd8.png

2.随机访问元素

通过指定下标可以快速访问数组中的特定元素,而不需要遍历整个数组,时间复杂度为O(1)。

如果我们想访问数组第三个元素,则:

#include<stdio.h>
int main() {
    int arr[10] = { 1, 2,3, 4, 5, 6, 7, 8 ,9, 10};
        printf("%d ", arr[2]);
    return 0;
}

运行结果为:

https://i-blog.csdnimg.cn/direct/eb653e0803644a13b2bcb4d370461899.png

3.修改指定元素

我们可以使用下标来更新数组中某个元素的值。这在需要修改数据 而不改变其他元素是非常有用的。

如果我们想修改数组第三个元素的值为1时,可以这样:

#include<stdio.h>
int main() {
    int arr[10] = { 1, 2,3, 4, 5, 6, 7, 8 ,9, 10};
    arr[2] = 1;
        printf("%d ", arr[2]);
    return 0;
}

运行结果为:

https://i-blog.csdnimg.cn/direct/c34a09155d544d52861e856cbab5ad24.png

4.查找某个元素

在某些情况下,可以通过比较值并使用下标来进行简单的查找操作。

如果你想查找是否存在数值为3的数组元素,可以这样:

#include<stdio.h>
int main() {
    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8 ,9, 10};
    int i = 0;
    for (i; i < 10; i++) {
        if (arr[i] == 3)
        {
            printf("找到了,下标为:%d\n", i);
            return 0;
        }
    }
    printf("你要查找的值不存在\n");
    return 0;
} 

运行结果:

https://i-blog.csdnimg.cn/direct/9b40bb112ae3421bb2901803cdbc7916.png

学到这,我们差不多会了简单的一维数组的使用了。

接下来,我们再看看一维数组是如何存储在内存中的。

六 数组在内存中的存储:

元素在内存中的存储是连续的,也就是紧挨着的。

如何证明?

这里就要用到一个取地址操作符’&',一个地址占位符’p

如下:

#include<stdio.h>
int main() {
    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8 ,9, 10};
    int i = 0;
    int flag = 0;
    for (i; i < 10; i++) {
        printf("%p\n", &arr[i]);
    }
    return 0;
}

运行结果:

https://i-blog.csdnimg.cn/direct/6dd86968ec6e45d4b5a5d02eb2b8d647.png


注:

  • 在64位机器(现在的大部分计算机)中地址的大小是8个字节;
  • '%p’占位符打印的是十六进制表示下的地址;
  • 两个十六进制位数字就占用一个字节

所以我们不难发现:整形数组的相邻数组元素首字节地址大小相差4个字节,也就是一个整型数据的大小。

所以我们可以得出:

  • 元素在内存中的存储是连续的,也就是紧挨着的。

七 变长数组

C语言在C99中引入了变长数组的概念,就是元素个数可以用变量表示的数组.。但有一些编译器不支持变长数组的语法:VS就是其中很经典的编译器。

例如:

int arr[n];
scanf("%d",&n);

变长数组的好处就是:

  • 1.数组的长度可以由自己输入确定;
  • 2.可以减少内存的浪费。

但也要注意:变长数组不可以初始化 如果想要初始化一个长度可以由自己改变的数组,则就要使用符号常量

例如:

#define  PI   10
int  arr[PI]={0};

首先,我们来看看二维数组的创建

八 二维数组的创建

知其然才能知其所以然,所以我们先来看看二维数组的概念

1. 二维数组的概念

基本概念:C语言的二维数组是一种用于存储具有行和列两个维度的数据集合的结构,类似于数学中的矩阵。在定义时,需要指定数组的行数和列数以及数据类型,例如int array[3][4];表示一个包含3行4列的整型二维数组。每个元素都可以通过其唯一的行索引列索引来访问和操作,非常适合处理表格形式的数据。

相信看过小编上篇关于一维数组的介绍的宝子们对索引一词并不会感到陌生,但这里的行索引列索引又是什么,难道是说明二维数组有两个不同功能的下标?带着这个疑惑,接着往下看。

知道这个二维数组的概念后,我们再来学习二维数组的创建

2. 二维数组的创建

由上易知格式为: 数据类型 数组名[行数][列数]

  • 行索引就是用来访问二维数组的行数,列索引就是用来访问二维数组的列数。具体怎么访问?我们之后再讨论。

以下为例:

int arr[2][3];
double arr1[3][4];

这样我们就创建了两个二维数组。

而事实上,二维数组在创建时是可以省略行数的,但一定不能省略列数。为什么?

其实你仔细思考就可以想到:

省略行数我们可以通过列数推到这个二维数组的行数,但是省略列数我们就得不到二维数组的列数了,没有列数的二维数组又怎么算做一个二维数组?

  • 所以我们在创建一个二维数组时不可省略列数,可以省略行数

所以以下的二维数组的创建是正确的

1   int  arr [][5] = { 1, 2, 3 };

2   int  arr [][6] = { 1, 2, 3, 4, 5, 6, 7, 8 };

3   int  arr [][7] = { {1, 2}, {3, 4}, {5, 6} };

而以下的二维数组的创建是错误的

1   int  arr [5][] = { 1, 2, 3 };

2   int  arr [6][] = { 1, 2, 3, 4, 5, 6, 7, 8 };

3   int  arr [7][] = { {1, 2}, {3, 4}, {5, 6} };
  • 好好利用这个知识点,可以在我们不知道我们将会创建多少个元素的二维数组但是知道列数时最大的节约内存空间但也可能造成语法错误。所以一定要牢记:二维数组在创建时是可以省略行数的,但一定不能省略列数

和一维数组一样,学完了二维数组的创建,我们来学二维数组的初始化

九 二维数组的初始化

初始化定义:在创建变量或数组的时候,给定一些初始值,被称为 初始化。二维数组的初始化与一维数组一样,也是用大括号初始化的。

同样地,二维数组的初始化也分为不完全初始化完全初始化,我们先来看看不完全初始化是怎样的。

1. 不完全初始化

顾名思义,不完全初始化就是数组元素并不全部进行赋值的赋值方式

以下为例:

#include<stdio.h>
int main(){
    int arr[2][3] = { 1, 2, 3, 4, 5 };
    return 0;
}

由上可知:

  • 该二维数组的行数列数分别为:【2】和【3】,所以我们不难得出该数组的数组元素的总数为6,而我们可以看到小编这里只对5个元素进行了赋值,是哪5个呢?

我们用监视窗口看一下:

https://i-blog.csdnimg.cn/direct/eacfef3910ee4a859c9065511a5028aa.png

由图可知:

  • arr[0][0],arr[0][1],arr[0][2],arr[1][0],arr[1][1]被分别初始化为1,2,3,4,5,只有 arr[1][2]没被赋值。所以我们不难看出:二维数组的初始化也是随着下标从小到大进行赋值的,只不过这里的下标有行下标和列下标,也就是行索引和列索引。并且似乎行索引对赋值顺序的决定作用更大。这里就先不深入讨论,后文会细讲。

看完了二维数组的部分初始化,我们再来学习二维数组的完全初始化

2. 完全初始化

说白了,就是对二维数组的全部元素进行了赋值

以下为例:

#include<stdio.h>
int main(){
    int arr[2][3] = { 1, 2, 3, 4, 5, 6};
    return 0;
}

这里同样用监视窗口看一下数组元素有没有被我们放进去初值:

https://i-blog.csdnimg.cn/direct/186a1419c69d45ab80d0bbe486dd589e.png

不出所料,相信这对于聪明的宝子们还是很简单的。那就再接着往下看。

接下来的就是一维数组不具备的独特初始化方式按照行初始化。那一起来看看吧!

3. 按照行初始化

其实,借助这个方法的名字——按照行初始化,我们不难想到这个初始化的方式是不是一行一行地赋初值?

是的,You are so good! ! ! 那该怎么达到我们想要的赋值效果呢? 这就要使用{}这个东东了。

以下为例:

#include<stdio.h>
int main(){
   int arr[2][3] = { {1}, {2, 3}};
   return 0;
}

如果没有使用{},此次的赋值结果由上可知应为arr[0][0] = 1,arr[0][1] = 2,arr[0][2] = 3,arr[1][0] = 0,arr[1][1] = 0,arr[1][2] = 0;

而使用了{}的实际结果如图:

https://i-blog.csdnimg.cn/direct/6fd280b939144da6986d47bb7caf456a.png

显然,这里的效果就是我们猜想的结果。

  • 按行初始化可以使得我们给二维数组初始化时更加灵活

在学完了二维数组的创建及初始化后,我们步入了二维数组的使用的学习。

十 二维数组的使用

当然,学习二维数组的使用毫无疑问从二维数组的下标开始学起。

1.二维数组的下标

在C语言中,二维数组可以被看作是一个表格(或矩阵),其中每个元素都有一个行号和列号。这些行号和列号就是所谓的下标,也就是行索引列索引的别称。而二维数组的下标也都是从零开始

假设我们有一个二维数组 arr[m][n],则:

  • m 表示数组的行数。
  • n 表示数组的列数。

那么,数组中的某个元素可以通过 arr[i][j] 来访问,其中:

  • i 是元素的行下标,取值范围是 0 到 m-1
  • j 是元素的列下标,取值范围是 0 到 n-1

例如,对于以下二维数组:

int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

要访问第二行第三列的元素(即数字7),可以使用 arr[1][2]

2.使用场景

基本使用场景:1. 表示矩阵: 二维数组非常适合用来表示数学上的矩阵。矩阵的运算(如加法、乘法)可以很方便地通过遍历和操作二维数组来实现。2. 图像处理:在图像处理中,图像通常被表示为像素点的集合。一个二维数组可以用来存储图像的灰度值或颜色信息(对于灰度图像,每个元素可以是亮度值;对于彩色图像,可能需要三个二维数组分别存储红、绿、蓝通道的值)。3. 动态规划问题:许多动态规划问题可以用二维数组来记录中间结果。例如,求解最长公共子序列(LCS)、最短路径等问题时,通常会使用一个二维数组来保存已经计算过的状态。4. 网格和地图:在游戏开发和其他应用中,二维数组可以用来表示游戏世界中的网格或地图。每个元素可以代表网格中的一个单元格,包含该单元格的信息(如是否可通行、是否有障碍物等)。5. 其他需要二维数据结构的场景: 除了上述场景外,还有许多其他问题也可以利用二维数组来解决。例如,统计数据的表格、棋盘游戏的状态表示等。

哈哈!宝子们是不是被吓了一跳,小编在刚看到时也被吓了一跳。是不是觉得有很多没听过的专业名词?没关系,小编在之后的日子里会带着大家把这些“拦路虎”一一征服!!!相信小编,也请相信自己!!!

在目前,我们需要掌握的就是二维数组的遍历

如何遍历?这里小编就以最简单的例子按行打印二维数组为例抛砖引玉:

#include<stdio.h>
int main(){
    int arr[2][3] = {1, 2, 3, 4, 5, 6};
    int i = 0;
    int j = 0;
    for (i; i < 2; i++) {
        for (j; j < 3; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

虽然例子简单,但小编想让大家感受到的是:

  • 一般我们在遍历二维数组时,都会使用两个整型变量ij并初始化为0。至于为什么初始化为0,这也是因为C语言的数组的下标从0开始
  • 且一般会使用两个循环,以for循环最为常见进行遍历

由于现在我们还处于初识二维数组的阶段,所以对二维数组的使用暂讲到这,大家理解完本文可以多去一些刷题网站 加深理解哦!

那现在就让我们进入本文的最后一章节—— 二维数组在内存中的存储

十一 二维数组在内存中的存储

这里小编就先从二维数组在内存中的存储结构开始讲起哈。

1.存储结构

从本质上看,二维数组可以被视为“数组的数组”。也就是说,它是一个一维数组的集合,其中每个元素本身又是一个一维数组。例如,int array[3][4]; 表示一个包含3个元素的二维数组,而每个元素都是一个包含4个整数的一维数组。

所以我们引申得到:arr[0]是第一行数组元素的数组名。这很重要,在我们后面理解一维数组和二维数组的数组名有大用。

接着是内存布局

2.内存布局

在内存中,二维数组是连续存储的。具体来说,二维数组的所有元素都按照行优先的顺序排列。这意味着,首先存储第一行的所有元素,紧接着是第二行的所有元素,以此类推,直到最后一行。这种存储方式使得我们可以通过简单的指针运算来访问数组中的任何元素。

这里小编也可以通过打印出一个二维数组的元素的所有地址验证上述说法:

#include<stdio.h>
int main(){
    int arr[2][3] = {1, 2, 3, 4, 5, 6};
    int i = 0;
    int j = 0;
    for (i; i < 2; i++) {
        for (j; j < 3; j++) {
            printf("%p\n", &arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

运行结果为:

https://i-blog.csdnimg.cn/direct/23aa239102b04e16a41821dbbb3a3e19.png

注:

  • 在64位机器(现在的大部分计算机)中地址的大小是8个字节;
  • '%p’占位符打印的是十六进制表示下的地址;
  • 两个十六进制位数字就占用一个字节

所以我们不难发现:整形数组的相邻数组元素首字节地址大小相差4个字节,也就是一个整型数据的大小。

所以我们可以得出:

  • 二维数组的元素在内存中的存储是连续的,也就是紧挨着的。这和一维数组是一样的

再接着来看看二维数组的访问方式

3.访问方式

由于二维数组在内存中是连续存储的,因此我们可以使用双重循环(嵌套循环)来遍历整个数组。外层循环用于迭代行,内层循环用于迭代列。通过指定行索引和列索引,我们可以轻松地访问或修改数组中的特定元素。

例:

#include<stdio.h>
int main(){
    int arr[2][3] = {1, 2, 3, 4, 5, 6};
    int i = 0;
    int j = 0;
    arr[1][2] = 7;
    for (i = 0; i < 2; i++) {
        for (j = 0; j < 3; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

运行结果为:

https://i-blog.csdnimg.cn/direct/b60a4b88f3724a1db4dd4a8f9d5add0f.png

我们这就通过二维数组的下标(索引)访问并修改了特定的元素的值。

最后我们来看看二维数组的地址计算

4.地址计算

在C语言中,数组名表示的是数组首元素的地址。对于二维数组来说,这个首元素实际上是一个指向其第一行第一个元素的一维数组的指针。因此,如果我们知道数组的首地址以及目标元素的行索引和列索引,就可以计算出该元素的内存地址。这通常涉及到一些基本的算术运算,如乘法(用于跳过前面的行或元素)和加法(用于定位到具体的元素)。

假设这有一个二维数组arr[2][3];,如果我们知道了首元素arr[0][0]的地址,那其他的元素的地址就可以很好的算出。不妨假设&arr[0][0] = 0000000000000000,则&arr[1][2] = 0000000000000018

为什么?这就需要宝子们自己思考啦!!!理解这题就掌握了这节知识——二维数组的内存布局和地址计算

  • 总的来说,C语言的二维数组在内存中是以连续的方式存储的,这使得它们非常适合于需要快速访问和修改大量数据的场景。

介绍到这,本文也已到了尾声,如果喜欢本篇文章,记得三连,要互三的宝子记得评论,小编怕遗漏!!!谢谢大家!!!


文章来源: https://blog.csdn.net/z15879084549/article/details/145259818
版权声明: 本文为博主原创文章,遵循CC 4.0 BY-SA 知识共享协议,转载请附上原文出处链接和本声明。


...全文
28 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

3,166

社区成员

发帖
与我相关
我的任务
社区描述
微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。
windowsmicrosoft 企业社区
社区管理员
  • 微软技术分享
  • 郑子铭
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。

予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。

试试用AI创作助手写篇文章吧