首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C语言基础:(五)数组

C语言基础:(五)数组

作者头像
_OP_CHEN
发布2026-01-14 09:04:11
发布2026-01-14 09:04:11
770
举报
文章被收录于专栏:C++C++

        数组是C语言中最基础且重要的数据结构之一,它允许程序员在单个变量中存储多个相同类型的元素。通过数组,可以高效地组织和访问大量数据,为算法实现和程序开发提供了极大的便利。         数组的连续内存存储特性使其在访问速度上具有优势,同时它也是其他复杂数据结构(如字符串、矩阵、链表等)的基础。从简单的数值存储到多维数据处理,数组在科学计算、嵌入式系统、游戏开发等领域广泛应用。         掌握数组的使用方法,不仅能够提升代码的简洁性和可读性,还能为后续学习指针、动态内存分配等高级概念打下坚实基础。         接下来让我们正式进入数组的学习吧!

一、数组的概念

数组是一组相同类型元素的集合。由这个概念我们可以发现以下两个有价值的信息:

  1. 数组中存放的是1个或者多个数据,但是数组元素个数不可为0.
  2. 数组中存放的多个数据,类型是相同的。

        数组可以分为一维数组和多维数组,多维数组中较为常见的是二维数组。

二、一维数组的创建和初始化

2.1  数组创建

        一维数组的创建的基本语法如下:

代码语言:javascript
复制
type arr_name[常量值];

        存放在数组中的值被称为数组的元素,数组在创建的时候可以指定数组的大小数组的元素类型

  • type 指定的是数组中存放数据的类型,可以是:char、short、int、float 等,也可以是自定义的类型。
  • arr_name 指的是数组名的名字,这个名字可以根据实际情况起,只要有意义就行。
  • [ ] 中的常量值是用来指定数组的大小的,这个数组的大小是根据实际的需求指定的。

        例如,假如我们想用数组储存在某个班级中的20人的数学成绩,那我们可以创建数组如下:

代码语言:javascript
复制
int math[20];

         当然,我们也可以根据具体需求创建不同类型和大小的数组:

代码语言:javascript
复制
char ch[8];
double score[10];

2.2  数组的初始化

        有时候,数组在创建时需要我们给定一些初始值,即需要对数组进行初始化。

        那么如何对数组进行初始化呢?一般我们需要使用大括号{ },将初始化数据放在大括号中。

代码语言:javascript
复制
//完全初始化
int arr[5] = {1,2,3,4,5};

//不完全初始化
int arr2[5] = {1,2,3};//前三个元素初始化为1、2、3,剩余的元素默认初始化为0

//错误的初始化 - 初始化项太多
int arr3[3] = {1, 2, 3, 4};

        在VS2022中,我们可以对已初始化的数组使用调试中的监视窗口,观察其中存放的元素:

2.3  数组的类型

        数组本身也是具有类型的,它算是一种自定义类型,去掉数组名后留下的就是数组的类型。 

        如下:

代码语言:javascript
复制
int arr1[10];
int arr2[12];

char ch[5];

        其中,arr1数组的类型为 int [10];arr2数组的类型是 int [12];ch数组的类型是 char [5]。

三、一维数组的使用

        我们已经学习了一维数组的基本语法,也知道了他可以存放数据,而存放数据的目的是对数据进行操作。那么我们应该如何使用一维数组呢?

3.1  数组下标

        C语言规定,数组是有下标的,且数组中的下标是从0开始的。即假设数组中有n个元素,那么第n个元素的下标就是n-1,下标就相当于是元素的编号,如下所示:

代码语言:javascript
复制
int arr[10] = {1,2,3,4,5,6,7,8,9,10};

        在C语言中还为数组的访问提供了一个操作符 [ ],这个操作符叫做下标访问操作符

        有了下标访问操作符之后,我们就可以轻松访问数组的元素,比如如果我们想要访问下标为7的元素,我们就可以使用 arr[7],想要访问下标为3的元素时,就可以使用 arr[3],代码如下:

代码语言:javascript
复制
#include <stdio.h>

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    printf("%d\n", arr[7]);//8
    printf("%d\n", arr[3]);//4
    return 0;
}

        输出结果如下:

3.2  数组元素的打印

        接下来,如果我们想要打印整个数组的内容,应该怎么办呢?

        其实很简单,我们只需要产生数组中所有元素的下标就可以了,那么我们可以使用for循环产生0~9的下标,然后再使用下标访问就可以了。

        代码如下:

代码语言:javascript
复制
#include <stdio.h>

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    for(i=0; i<10; i++)
    {
        printf("%d ", arr[i]);
    } 
    return 0;
}

         输出结果如下:

 3.3  数组的输入

        明白了数组的访问,我们也可以根据需求,自己将数据输入到数组中。如下代码所示,我们想数组中输入十个整数,再把这些整数打印出来:

代码语言:javascript
复制
#include <stdio.h>

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    for(i=0; i<10; i++)
    {
        scanf("%d", &arr[i]);
    } 
    for(i=0; i<10; i++)
    {
        printf("%d ", arr[i]);
    } 
    return 0;
}

        输出结果如下:

四、一维数组在内存中的存储

        有了前文所述的知识,我们对于数组的使用基本就没什么障碍了,但如果我们想要进一步深入地了解数组,我们最好能够了解一下数组在内存中的存储。

        我们先来依次打印数组中元素的地址:

代码语言:javascript
复制
#include <stdio.h>

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

         输出结果如下:

        观察输出结果,我们发现,数组随着下标的增长,地址是从小到大变化的,且每两个相邻的元素地址之间相差为4(因为一个整型是4个字节)。所以我们可以得出结论:数组在内存中是连续存放的。 这就为后期我们使用指针访问数组奠定了基础(指针是指向元素的地址的,我们在后续的博客中会详细进行讨论)。

五、sizeof计算数组元素个数

        在遍历数组的时候,我们常常想知道数组的元素个数,那么在C语言中是否存在一种方法使用程序计算数组元素个数呢?

        答案是有的,这个方法就是 sizeof 函数 。

        sizeof 在C语言中是一个关键字,是可以计算类型或者变量的大小的,也可计算数组大小。

        代码如下:

代码语言:javascript
复制
#include <stido.h>

int main()
{
    int arr[10] = {0};
    printf("%d\n", sizeof(arr));
    return 0;
}

        这个程序的输出结果是40,它所计算的是数组所占内存空间的总大小,单位是字节

        我们又知道在数组中所有的元素的类型都是相同的,那么我们只要计算出一个元素所占字节的个数,数组的元素个数就可以计算出来。这里我们选择第一个元素计算大小就可以了。

代码语言:javascript
复制
#include <stido.h>

int main()
{
    int arr[10] = {0};
    printf("%d\n", sizeof(arr[0]));//计算⼀个元素的⼤⼩,单位是字节
    return 0;
}

        接下来我们就可以计算得出数组中的元素个数:

代码语言:javascript
复制
#include <stido.h>

int main()
{
    int arr[10] = {0};
    int sz = sizeof(arr)/sizeof(arr[0]);
    printf("%d\n", sz);
    return 0;
}

        这里的结果是:10,表示数组有10个元素。其中,sizeof(arr)/sizeof(arr[0]) 表示用数组的大小除以数组中第一个元素的大小,所得的结果即为数组的元素个数。

        这样以后在代码中,如果需要数组元素个数的地方就可以直接使用上面的计算,不论数组怎么变化,计算出的大小也就可以随之变化了。

六、二维数组的创建

6.1  二维数组的概念

        我们前面学习的数组是一维数组,在数组中的元素全都是内置类型的,但如果我们把一维数组当作数组的元素,这时候就成为了二维数组,如果把二维数组作为数组元素的数组就被称为三维数组,二维数组以上的数组统称为多维数组。下为整型、整型一维数组和整型二维数组的示意图:

6.2  二维数组的创建

        我们应该如何定义二维数组呢?参考语法如下:

代码语言:javascript
复制
type arr_name[常量值1][常量值2];

例如:
int arr[3][5];
double data[2][8];

         对上述代码,编者给出以下解释:

  • 3 表示数组有3行
  • 5 表示每一行有5个元素
  • int 表示数组的每个元素是整型类型
  • arr、data 是数组名,可根据具体需要指定名字

七 、二维数组的初始化

        那么二维数组又是如何实现初始化的呢?像一维数组一样,它也是使用大括号进行初始化的。同样,二维数组的初始化也可以分为不完全初始化完全初始化

7.1  不完全初始化

        与一维数组类似,二维数组的不完全初始化也是将部分元素初始化为特定值,将剩余的元素默认初始化为0。

代码语言:javascript
复制
int arr1[3][5] = {1,2};
int arr2[3][5] = {0};

7.2  完全初始化 

        完全初始化则是将全部元素都初始化为特定值。

代码语言:javascript
复制
int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};

7.3  按照行进行初始化

        在二维数组中,一个元素即为二维数组中的一行。

代码语言:javascript
复制
int arr4[3][5] = {{1,2},{3,4},{5,6}};

7.4  初始化时省略行,但是不能省略列

代码语言:javascript
复制
int arr5[][5] = {1,2,3};
int arr6[][5] = {1,2,3,4,5,6,7};
int arr7[][5] = {{1,2}, {3,4}, {5,6}};

八、二维数组的使用

8.1  二维数组的下标

        现在我们已经掌握了二维数组的创建和初始化,那么二维数组又应该如何使用呢?

        其实二维数组访问也是使用下标形式的,顾名思义,二维数组在空间上是二维的,具有行和列,因此只要锁定了行和列,我们就能唯一锁定数组中的一个元素。

        C语言对二维数组作出了规定:二维数组的行是从0开始的,列也是从0开始的,如下所示:

代码语言:javascript
复制
int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};

        图中最左侧绿色的数字表示行号,第一行蓝色的数字表示列号,它们都是从0开始的,比如,当我们想找到第2行、第4列的数字,就能快速定位到7。代码如下:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
    printf("%d\n", arr[2][4]);
    return 0;
}

         输出的结果如下:

8.2  二维数组的输入和输出

        我们知道了如何访问二维数组的单个元素,那么又应该如何访问整个二维数组呢?

        实际上我们只要能按照一定的规律产生所有的行和列的数字就行。以上一段代码中的arr数组为例,行的选择范围是0~2,列的取值范围是0~4,所以我们可以借助循环实现生成所有的下标。

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
    int i = 0;//遍历⾏
    //输⼊
    for(i=0; i<3; i++) //产⽣⾏号
    {
        int j = 0;
        for(j=0; j<5; j++) //产⽣列号
        {
            scanf("%d", &arr[i][j]); //输⼊数据
        }
    } 
    //输出
    for(i=0; i<3; i++) //产⽣⾏号
    {
        int j = 0;
        for(j=0; j<5; j++) //产⽣列号
        {
            printf("%d ", arr[i][j]); //输出数据
        } 
        printf("\n");
    } 
    return 0;
}

        输入和输出的结果如下:

九、二维数组在内存中的存储

        如同一维数组一样,我们如果想要研究二维数组在内存中的储存方式,我们也可以打印出数组中所有元素的地址。代码如下所示:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int arr[3][5] = { 0 };
    int i = 0;
    int j = 0;
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 5; j++)
        {
            printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
        }
    } 
    return 0;
}

        输出的结果为:

         从输出的结果来看,每一行内部的每个元素在内存中都是相邻的,它们的地址之间相差了4个字节,在跨行位置处的两个元素(如:arr[0][4]和arr[1][0])之间也同样相差了4个字节,所以二维数组中的每个元素都是连续存放的

        如下图所示:

        我们必须了解清楚二维数组在内存中的布局,这样有利于后期我们对于使用指针来访问数组的理解和学习。

 十、C99中的变长数组

        在C99标准之前,C语言在创建数组时,数组大小的指定只能使用常量或者常量表达式,或者我们对数组先进行初始化,可以省略数组大小。但这样的语法限制会使我们创建数组不够灵活,有时候数组大了就会浪费空间,有时候数组小了又会使得内存空间不够用。

        在C99中给了一个变长数组(variable-length array,简称VLA)的新特性,允许我们可以使用变量指定数组的大小。代码如下所示:

代码语言:javascript
复制
int n = a+b;
int arr[n];

        在上面的示例中,数组arr就是变长数组,因为它的长度取决于变量n的值,而编译器无法事先确定n的值,只有运行时才能知道n是多少。

        变长数组的根本特征,就是数组长度只有运行时才能确定,所以变长数组不能初始化。它的好处是程序员不必在开发时随意为数组指定一个估计的长度,程序可以在运行时为数组分配精确的长度。对于初学者而言可能会有一个比较迷惑的点,那就是变长数组的意思是可以使用变量来指定数组长度,接着在程序运行的时候,根据变量的大小来指定数组中的元素个数,而并不是说数组的大小是可变的。数组的大小一旦确定就不能再变化了。

        令人遗憾的是,在VS2022内置的msvc编译器上,虽然支持大部分C99的语法,但却并不支持C99中的变长数组,无法进行测试。下面是在gcc编译器上测试的代码,如下所示:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int n = 0;
    scanf("%d", &n);//根据输⼊数值确定数组的⼤⼩
    int arr[n];
    int i = 0;
    for (i = 0; i < n; i++)
    {
        scanf("%d", &arr[i]);
    } 
    for (i = 0; i < n; i++)
    {
        printf("%d ", arr[i]);
    } 
    return 0;
}

        第一次测试时,我给n中输入5,然后再输入5个数字在数组中,并正常输出;

        第二次测试,我又给n中输入10,然后输入10个数字在数组中,并正常输出。输出结果如下:

        可以看到gcc编译器是支持变长数组的,在编写代码时,变长数组常常能发挥很大用处。 

十一、数组相关练习

练习1:多个字符从两端移动,向中间汇聚 

        编写代码,实现多个字符从两端开始移动,并不断向中间汇聚。

        代码如下:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    char arr1[] = "welcome to my blog!";
    char arr2[] = "####################";
    int left = 0;
    int right = strlen(arr1)-1;
    printf("%s\n", arr2);
    while(left<=right)
    {
        Sleep(1000);
        arr2[left] = arr1[left];
        arr2[right] = arr1[right];
        left++;
        right--;
        printf("%s\n", arr2);
    } 
    return 0;
}

        需要注意的是,我们所使用的arr1数组和arr2数组长度必须匹配,否则在运行时会出现如下栈溢出的编译错误

练习2:二分查找

        在一个升序的数组中查找指定的数字n,我们比较容易想到的方法就是遍历数组,但是这种方法效率比较低。

        比如我买了一双鞋,你出于好奇问我多少钱,我说不超过300元。但是你还是好奇,你想知道到底多少钱,我让你猜,你会怎么猜?如果你按照1,2,3,4......这样一个一个猜的话显然很慢,我们一般都会从中间数字猜起,比如150,然后看是大了还是小了,这便是二分(折半)查找

        我们可以按照如下逻辑来编写二分查找的代码:

  1. 确定被查找范围的左右下标;
  2. 求中间元素的下标;
  3. 锁定中间元素 和 n 比较:
  • 中间元素 arr[M] < n , left = M+1
  • 中间元素 arr[M] > n , right = M-1
  • 中间元素 arr[M] == n,说明找到了

         代码如下所示:

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    int left = 0;
    int right = sizeof(arr)/sizeof(arr[0])-1;
    int key = 7;//要找的数字
    int mid = 0;//记录中间元素的下标
    int find = 0;
    while(left<=right)
    {
        mid = (left+right)/2;
        if(arr[mid]>key)
        {
            right = mid-1;
        } 
        else if(arr[mid] < key)
        {
            left = mid+1;
        } 
        else
        {
            find = 1;
            break;
        }
    } 
    if(1 == find )
        printf("找到了,下标是%d\n", mid);
    else
        printf("找不到\n");
}

       对于中间元素的下标, mid = (left + right) / 2 这种方式可能会导致整数溢出的问题。当 leftright 都是非常大的整数时,它们的和可能超过 int 类型的最大值,从而导致溢出。这种溢出在某些编程语言中可能导致未定义的行为或错误。

        因此,我们可以采用mid = left + (right - left) / 2 这种方式。它通过先计算差值再除以 2 来避免直接相加可能引起的溢出问题。这确保了即使在非常大的数组中,计算也不会超出 int 类型的范围。因此,这种方式更加安全可靠,尤其是在处理大规模数据集时。并且从性能角度来看,这两种方法之间的差异通常可以忽略不计。代码如下所示:

代码语言:javascript
复制
mid = left+(right-left)/2;

        以上就是本期博客的全部内容啦,希望喜欢我的朋友可以多多点赞收藏+关注!后续我会不断为大家更新更多的优质内容滴! 

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、数组的概念
  • 二、一维数组的创建和初始化
    • 2.1  数组创建
    • 2.2  数组的初始化
    • 2.3  数组的类型
  • 三、一维数组的使用
    • 3.1  数组下标
    • 3.2  数组元素的打印
    •  3.3  数组的输入
  • 四、一维数组在内存中的存储
  • 五、sizeof计算数组元素个数
  • 六、二维数组的创建
    • 6.1  二维数组的概念
    • 6.2  二维数组的创建
  • 七 、二维数组的初始化
    • 7.1  不完全初始化
    • 7.2  完全初始化 
    • 7.3  按照行进行初始化
    • 7.4  初始化时省略行,但是不能省略列
  • 八、二维数组的使用
    • 8.1  二维数组的下标
    • 8.2  二维数组的输入和输出
  • 九、二维数组在内存中的存储
  •  十、C99中的变长数组
  • 十一、数组相关练习
    • 练习1:多个字符从两端移动,向中间汇聚 
    • 练习2:二分查找
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档