专栏首页C语言中文社区C语言从青铜到王者——数组详解【一维数组、二维数组、字符数组】
原创

C语言从青铜到王者——数组详解【一维数组、二维数组、字符数组】

所谓数组,是指将那些具有相同类型的、数量有限的若干个变量通过有序的方法组织起来的一种便于使用的形式。数组属于一种构造类型,其中的变量被称为数组的元素。数组元素的类型可以是基本数据类型,也可以是特殊类型和构造类型。

一维数组

一位数组是最简单的数组类型,它的定义形式如下:

类型说明符 数组名[常量表达式]

类型说明符是数组中每个元素的类型,常量表达式是数组元素的个数

在使用一维数组的时候需要留意以下两个要点

  • 常量表达式的值必须是正整数
  • 数组元素的引用,数组的起始元素下标为0

下来我们通过一个简单的示例了解一下数组

代码如下:

//公众号:C语言中文社区
#include<stdio.h>
#define N 9
int main(void) {
    int arr[N];
    int i;
    for (i = 0; i < N; i++)
    {
        arr[i] = i + 1;
        printf("arr[%d]=%d\t", i, arr[i]);
        if (0 == (i+1)%3)
        {
            printf("\n");
        }
    }
    return 0;
}

运行结果如下:

image

我们分析一下上面这段代码

我们定义了一个含有9个元素的一位数组arr,在引用数组中的元素时,采用"数组名下标"的方式,将其中的每一个元素视为一个普通的变量来进行操作。需要注意的是,因为定义的数组arr仅含有9个元素,所以在使用的过程中,下标值不能超过8,否则就会出现下标越界的错误,示例如下:

image

在使用数组的时候要特别注意数组越界,不然很有可能为自己埋下一颗雷(bug)。

接下来我们我们通过一段代码看一下数组在内存中是如何存放的

//公众号:C语言中文社区
#include<stdio.h>
#define N 4
int main(void) {
    int arr[N];
    int i;
    for (i = 0; i < N; i++)
    {
        arr[i] = i;
        printf("&arr[%d]=%d\n", i, &arr[i]);
    }
    return 0;
}

运行结果如下:

image

从结果我们可以看出,每个元素占用4个字节,在内用中的存储结构图如下:

image

最后我们再通过一个示例来巩固一下一维数组

需求:使用数组保存用户输入的数据,当输入完毕后逆向输出

代码如下:

//公众号:C语言中文社区
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define N 5
int main(void) {
    int arr[N];//定义数组
    int i, temp;//定义变量
    printf("请输入一个5个元素数组:\n");
    for (i = 0; i < N; i++)
    {
        scanf("%d", &arr[i]);
    }
    printf("读取到的数组如下:\n");
    for (i = 0; i < N; i++)
    {
        printf("%d ",arr[i]);
    }
    printf("\n");
    for (i = 0; i < 2; i++)//将数组中元素的前后位置互换
    {
        temp = arr[i];
        arr[i] = arr[4 - i];
        arr[4 - i] = temp;
    }
    printf("输出的逆向数组如下:\n");
    for (i = 0; i < N; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

运行结果如下:

image

二维数组

二维数组定义的一般形式如下:

类型说明符 数组名[常量表达式1][常量表达式2]

与一维数组的定义唯一的不同是多了一个常量表达式2,其中,常量表达式1为第一维的长度,常量表达式2为第二维的长度。通常在处理二维数组的时候,为了便于理解,都将数组视为一个矩阵,常量表达式1表示矩阵的行数,而常量表达式2表示矩阵的列数。与一维数组一样,在定义二维数组时,常量表达式同样不能为变量。下面先通过一段代码来看二维数组的定义。

//公众号:C语言中文社区
#include<stdio.h>

#define M 4
#define N 3


int main() {
  int arr[M][N];
  for (int i = 0; i < M; i++)
  {
    for (int j = 0; j < N; j++)
    {
      printf("&arr[%d][%d]=%d\t", i, j, &arr[i][j]);
    }
    printf("\n");
  }
  return 0;
}

运行结果:

将二维数组arr视为一个矩阵,下图显示了数组中每个元素在矩阵中的存放位置。

数组中各个元素在矩阵中对应的位置由二维数组的两个下标决定。我们可以将定义的二维数组int arr4视为由arr4和int 3 两部分构成,将arr4视为一个整型一维数组,其中含有4个元素arr0、arr1、arr2、arr3,每个元素都是int3类型的,也就是说,每个元素又是一个一维数组,每个一维数组含有3个元素,如arr0含有arr0、arr0、arr0三个元素。

知道了二维数组的这种特殊结构之后,接下来通过下图来了解二维数组在内存中的存储结构。

通过上述二维数组在内存中的存储结构图可以发现,二维数组中的所有元素都存储在一片连续的内存单元中,所占用的内存大小为元素类型所占用的内存大小乘以第一维及第二维的长度。如果以矩阵的方式来分析二维数组的存储方式,那么先从矩阵第一行从左往右依次存储完所有元素,然后按照同样的方法存储第二行的所有元素,直到存储完所有数组元素为止。

接下来再看一个二维数组的示例:

任意输入一个3行3列的二维数组,求对角元素之和

//公众号:C语言中文社区
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

int main() {
  int arr[3][3];
  int i, j, sum = 0;
  printf("please input:\n");
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 3; j++)
    {
      scanf("%d", &arr[i][j]);
    }
  }
  for (i = 0; i < 3; i++)
  {
    for ( j = 0; j < 3; j++)
    {
      if (i==j)
      {
        sum += arr[i][j];
      }
    }
  }
  printf("the result is: %d\n", sum);
  return 0;
}

运行结果如下:

字符数组

字符数组顾名思义就是数组的元素类型为字符型的数组。特殊之处在于它是数组元素为字符的数组。其定义的一般形式和注意事项与之前讲解的一般数组类似,只是其中的类型说明符是char。当然,并不是说类型说明符只能是char,也可以是long、int等,但是由于char型只占用一个字节的大小,使用long型和int型来定义字符数组会造成资源的浪费,因此一般选择使用char型来定义字符数组。

一维字符数组

首先通过下面一段代码来看看一维字符数组的定义。

//公众号:C语言中文社区
#include<stdio.h>
#define SIZE 20
int main() {
    long arr1[SIZE] = {'h','e','l','l','o',' ','w','o','r','l','d','!'};
    char arr2[SIZE] = { 'h','e','l','l','o',' ','w','o','r','l','d','!' };
    printf("long型字符数组占用的内存大小为:%d\n", sizeof(arr1));
    printf("char型字符数组占用的内存大小为:%d\n", sizeof(arr2));
    return 0;
}

运行结果:

在上面的代码中定义了不同类型的字符数组来存放相同的字符,可以看出,它们占用的内存大小相差很大,long型字符数组所占用内存大小是char型数组占用内存大小的4倍。从这点可以看出,选用char型作为数组类型避免了内存空间的浪费。下面通过一段代码来了解字符数组的初始化特点。

//公众号:C语言中文社区
#include<stdio.h>
#define SIZE 20
int main() {
    int i;
    char arr[SIZE] = { 'h','e','l','l','o',' ','w','o','r','l','d','!' };
    for (i = 0; i < SIZE; i++)
    {
        printf("%c", arr[i]);
    }
    
    return 0;
}

运行结果:

运行结果为“Hello World!”,其中有一些空字符。看看上面代码中定义的arr数组,其数组长度为20,而初始化的字符元素的个数为12,初始化的字符元素个数小于数组长度,编译器在编译过程中将后面没有初始化的数组元素赋值为‘\0’,这也正是打印输出中含有空字符的原因。在打印的时候也可以将数组中的元素‘\0’视为数组结束的标志,例如:

//公众号:C语言中文社区
#include<stdio.h>
#define SIZE 20
int main() {
    int i;
    long arr[SIZE] = { 'h','e','l','l','o',' ','w','o','r','l','d','!' };
    for (i = 0; arr[i]!='\0'; i++)
    {
        printf("%c", arr[i]);
    }
    
    return 0;
}

运行结果:

这时的输出结果中就不含有任何空字符了,因为巧妙地使用了字符数组中的‘\0’标志。当然,也可以采用字符串常量的方式来对一维字符数组进行初始化,例如:

//公众号:C语言中文社区
#include<stdio.h>
#define SIZE 20
int main() {
    int i;
    char arr[SIZE] = { "hello world!" };
    for (i = 0; arr[i] != '\0'; i++)
    {
        printf("%c", arr[i]);
    }
    
    return 0;
}

运行结果:

在对一维字符数组进行定义和初始化的过程中,可以不指定其长度。使用字符常量列表和字符串常量的方式进行初始化的结果是不同的,例如:

//公众号:C语言中文社区
#include<stdio.h>
int main() {
    int i;
    char arr1[] = { "hello world!" };
    char arr2[] = {'h','e','l','l','o',' ','w','o','r','l','d','!'};
    
    printf("采用字符串常量进行初始化的arr1数组的长度为:%d\n", sizeof(arr1));
    printf("采用字符常量列表进行初始化的arr2数组的长度为:%d\n", sizeof(arr2));
    
    return 0;
}

运行结果:

从运行结果发现,采用这两种方式得到的数组长度并不相同,在采用字符串常量对字符数组进行初始化的过程中,在内存中进行存储时会自动在字符串的后面添加一个结束符‘\0’,所以得到的字符数组长度是字符串常量的长度加1;而采用字符常量列表的方式对字符数组进行初始化就不会在最后添加一个结束符,所以利用这种方式定义的字符数组的长度就是字符常量列表中字符的个数。

数组实例

交换数组中最大数和最小数的位置

实例代码

//
// Created by 冲哥 on 2021/22/09.
// 实现功能:交换数组中最大数和最小数的位置
//
//公众号:C语言中文社区
#include "stdio.h"

int main(){
    int a[10];
    int max, min;
    int m, n;

    printf("请输入10个数字:\n");
    for (int i = 0; i < 10; i++) {
        scanf("%d", &a[i]);
    }
    printf("输入的10个数是:\n");
    for (int i = 0; i < 10; i++) {
        printf("%4d", a[i]);
    }
    printf("\n");
    max = a[0];
    for (int i = 0; i < 10; i++) {
        if (a[i] > max) {
            max = a[i];
            m = i;
        }
    }

    min = a[0];
    for (int i = 0; i < 10; i++) {
        if (a[i] < min) {
            min = a[i];
            n = i;
        }
    }

    a[m] = min;
    a[n] = max;

    printf("交换最大数和最小数的位置后:\n");
    for (int i = 0; i < 10; i++) {
        printf("%4d", a[i]);
    }
} 

运行结果

程序分析

首先找到数组中的最大值和最小值,记录它们的位置,然后交换位置,最后将交换后的数组输出。

更多C语言干货,请微信搜索【C语言中文社区】

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 不会用的Java数组,从青铜到王者,全解析数组,建议收藏!!!

    数据类型 [] 数组名称 = new 数据类型[长度];String[] arr3 = new String[5];

    香菜聊游戏
  • C语言入门系列之6.一维和二维数组

    在程序设计中,为了处理方便,把具有相同类型的若干变量按有序的形式组织起来,这些按序排列的同类数据元素的集合称为数组。 在C语言中,数组属于构造数据类型; 数...

    cutercorley
  • 江哥带你玩转C语言| 12 -二维数组和字符串

    如果觉得文章对你有帮助,点赞、收藏、关注、评论,一键四连支持,你的支持就是江哥持续更新的动力。

    极客江南
  • C语言 二维数组和指针的一些笔记

    &arr是指整个数组的首地址,而arr是指数组首元素的首地址,虽然所表示的意义不同,但二者之间的值却是相同的。

    小锋学长
  • C语言 | 将一个二维数组行列元素互换

    解题思路:可以定义两个数组:数组a为2行3列,存放指定的6个数。数组b为3行2列,开始时未赋值,只要将a数组中的元素ai存放到b数组中的bj元素中即可。

    小林C语言
  • C语言经典100例002-将M行N列的二维数组中的字符数据,按列的顺序依次放到一个字符串中

    Python编程爱好者
  • 基于DOS的简易俄罗斯方块制作

    制作一个简易的俄罗斯方块,未调用graphics库(装了半天不成功),完全的黑框操作 基本思路: 1.利二维数组表示地图(也可以利用一维数组,此处利用的是一维数...

    用户7886150
  • 一起回归一下每日一题这些经典面试题

    面试造火箭,工作拧螺丝,虽然我只想拧螺丝,可是我需要用造火箭的技术去寻找拧螺丝的工作,如何能在面试过程中让自己处于不败的地步呢,刷题是一个比较好的捷径,今天就汇...

    用户1308196
  • 转行程序员能干嘛?

    2. 可以做一个自由职业开发者,做个网站,开发个app,能不能捞金就看个人了,反正B是装完了。

    王炸
  • 跟AI学口语,鹅厂“神器”了解一下

    ? 朋友们,今天鹅老师要给大家上一堂英语课。now,我们先用一组chinglish热身一下,请跟我read: Give you some color see ...

    鹅老师
  • 12个Python常用基础语法,你会几个?

    前几天写了一篇关于python高级语法的文章,青铜到王者,这些python技巧一定会用上。

    Java架构师必看
  • Java面试官:double精度真的比float低吗?

    我有一个朋友,叫老刘,戴着度数比我还高的近视镜,显得格外的“程序员”;穿着也非常“不拘一格”,上半身是衬衣西服,下半身是牛仔裤运动鞋。

    沉默王二
  • 2021你集五福了吗?背后的Web3D引擎Oasis Engine正式开源!

    相信大家已经体验了今年支付宝五福的活动,无论是今年的五福首页还是打年兽游戏都是由蚂蚁互动图形引擎(代号:Oasis Engine)驱动的。

    Java程序猿
  • 2015 GOPS:全球运维大会·上海站全程实录(附赠精彩演讲PPT)

    2015年10月31日,以“运维2.0:行业的思与辩”为主题的2015 GOPS:全球运维大会 · 上海站 隆重开幕。本次大会由资深运维从业人士发起,四大运维体...

    数据和云
  • 写算法,用 C++ 还是用 Java ,差别大吗?

    今天带来的文章,是 GitChat 签约作者王晓华在不断被读者吐槽:“好好一本算法书为什么要用 C++ 来写” 时,万般无奈下憋出来的。

    用户1737318
  • 腾讯云学院直播课丨云数据库之从青铜到王者

    4月10日,腾讯云学院邀请到了讲师刘迪,进行了一场直播课,课程主题是:云数据库之从青铜到王者。整个直播课精彩纷呈,广受好评。

    云加社区
  • 密码学之恺撒加密(03)

    公元683年,唐中宗即位。随后,武则天废唐中宗,立第四子李旦为皇帝,但朝政大事均由她自己专断。

    海拥
  • 国人当自强 | 中国古代的“机器人梦”,带你看看祖先们的机械智慧

    今天,机器人可以被设计、制作,为人类服务。而在中国,古代人早就有“机器人梦”,梦想着能制作一种形态像人的物件来代替人类劳动。为这个梦想,中国人制作出了能够自己活...

    机器人网
  • 后端程序员必会:并发情况下redis-lua保证原子操作

    本文主要是分享在实际工作中同事遇到的问题案例;活动组在做活动时,开发人员未考虑到接口并发场景,导致因为一些用户在实际抽奖(土豪一般都是狂抽)过程中对余额产生了增...

    我是阿沐

扫码关注云+社区

领取腾讯云代金券