《小菜狗 C 语言入门 + 进阶笔记》(30)二维数组进阶


《小菜狗 C 语言入门 + 进阶笔记》目录:《小菜狗 C 语言入门 + 进阶笔记》(0)简介

1、二维数组的下标

其实二维数组访问也是使用下标的形式的,二维数组是有行和列的,只要锁定了行和列就能唯⼀锁定数组中的⼀个元素。

二维数组的行是从 0 开始的,列也是从 0 开始的,如下所示:

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

在这里插入图片描述

图中最右侧绿色的数字表示行号,第⼀行蓝色的数字表示列号,都是从 0 开始的。

比如:

第 0 行,第 2 列,a[0][2] = 3;

第 2 行,第 4 列,a[2][4] = 7;

第 2 行,第 0 列,a[2][0] = 3;

2、访问二维数组元素

二维数组中的元素是通过使用下标(即数组的行索引和列索引)来访问的。例如:

int val = a[2][3];

让我们来看看下面的程序,我们将使用嵌套循环来处理二维数组:

#include <stdio.h>
 
int main()
{
   /* 一个带有 5 行 2 列的数组 */
   int a[5][2] = { {0,0}, {1,2}, {2,4}, {3,6},{4,8}};int i, j;
   /* 输出数组中每个元素的值 */
   for ( i = 0; i < 5; i++ )
   {
      for ( j = 0; j < 2; j++ )
         printf("a[%d][%d] = %d\n", i,j, a[i][j] );}
   }
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

a[0][0] = 0
a[0][1] = 0
a[1][0] = 1
a[1][1] = 2
a[2][0] = 2
a[2][1] = 4
a[3][0] = 3
a[3][1] = 6
a[4][0] = 4
a[4][1] = 8

3、二维数组的输入

举例:

#include <stdio.h>
int main()
{
    int arr[2][3] = {0};
    int i = 0;//遍历行
    //输入
    for(i=0; i<2; i++) //产⽣行号
    {
        int j = 0;
        for(j=0; j<3; j++) //产⽣列号
        {
            scanf("%d", &arr[i][j]); //输入数据
        }
    }
    //输出
    for(i=0; i<2; i++) //产⽣行号
    {
        int j = 0;
        for(j=0; j<3; j++) //产⽣列号
        {
            printf("%d ", arr[i][j]); //输出数据
        }
        printf("\n");
    }
    return 0;
}

输入和输出的结果:

1 1 1 2 2 2
1 1 1
2 2 2

扩展 1 – 二维数组存放字符串

二维数组存放字符串,读取时当一维数组使用。

举例:

#include<stdio.h>

int main()
{
  int i;
  char names[6][50] = {"马超", "关平", "赵云", "张飞", "关羽", "刘备"};
  for (i=0; i<6; i++) {
      printf("悍将名称:%s\n", names[i]);
  }
  return 0;
}

运行结果:

悍将名称:马超
悍将名称:关平
悍将名称:赵云
悍将名称:张飞
悍将名称:关羽
悍将名称:刘备

扩展 2 – 二维数组名的步长

二维数组在逻辑上是方阵,由行和列组成。

但是二维数组在物理上是线性的,按行来依次进行存放,内存是连续的。

举例:

#include <stdio.h>

int main()
{
    int age[6][4];
    for (int i = 0; i < sizeof(age)/sizeof(age[0]) ; i++)
    {
        printf("age + %d address is %p\n",i, age + i);
    }
    for (int i = 0; i < sizeof(age) / sizeof(age[0]); i++)
    {
        for (int j = 0; j < sizeof(age[0]) / sizeof(int); j++)
        {
            printf("age[%d][0] + %d address is %p\n", i, j, &age[i][0]+j);
        }
    }
    return 0;
}

运行结果:

age + 0 address is 0x7fffd98b9400
age + 1 address is 0x7fffd98b9410
age + 2 address is 0x7fffd98b9420
age + 3 address is 0x7fffd98b9430
age + 4 address is 0x7fffd98b9440
age + 5 address is 0x7fffd98b9450
age[0][0] + 0 address is 0x7fffd98b9400
age[0][0] + 1 address is 0x7fffd98b9404
age[0][0] + 2 address is 0x7fffd98b9408
age[0][0] + 3 address is 0x7fffd98b940c
age[1][0] + 0 address is 0x7fffd98b9410
age[1][0] + 1 address is 0x7fffd98b9414
age[1][0] + 2 address is 0x7fffd98b9418
age[1][0] + 3 address is 0x7fffd98b941c
age[2][0] + 0 address is 0x7fffd98b9420
age[2][0] + 1 address is 0x7fffd98b9424
age[2][0] + 2 address is 0x7fffd98b9428
age[2][0] + 3 address is 0x7fffd98b942c
age[3][0] + 0 address is 0x7fffd98b9430
age[3][0] + 1 address is 0x7fffd98b9434
age[3][0] + 2 address is 0x7fffd98b9438
age[3][0] + 3 address is 0x7fffd98b943c
age[4][0] + 0 address is 0x7fffd98b9440
age[4][0] + 1 address is 0x7fffd98b9444
age[4][0] + 2 address is 0x7fffd98b9448
age[4][0] + 3 address is 0x7fffd98b944c
age[5][0] + 0 address is 0x7fffd98b9450
age[5][0] + 1 address is 0x7fffd98b9454
age[5][0] + 2 address is 0x7fffd98b9458
age[5][0] + 3 address is 0x7fffd98b945c

详细说明:

二维数组名的步长是一行的长度,比如一下例子中:

age + 1 address is 00EFFC04
age + 2 address is 00EFFC14

因为每一行有四个元素,每个 int 类型的元素占四个字节,一行有 16 个字节,所以数组名 age 加 1 后地址增加了 16 个字节说明数组名的步长位一行的长度。

具体到每一个元素加 1 的时候,地址增加的是一个元素所占字节的大小,因此元素的步长即为元素本身的大小,例如:

age[2][0] + 0 address is 00EFFC14
age[2][0] + 1 address is 00EFFC18

扩展 3 – 二维数组当作参数

将二维数组当作参数的时候必须指明所有维数大小或者省略第一维的,但是不能省略第二维或者更高维的大小,这是由编译器原理限制的。

事实上,编译器是这样处理数组的:

设有数组 int a[m][n],如果要访问 a[i][j]的值,编译器的寻址方式为:

&a[i][j] = &a[0][0] + i*sizeof(int)*n + j*sizeof(int); 
// 注意 n 为第二维的维数

因此,可以省略第一维的维数,不能省略其他维的维数。

在定义二维数组的时候对其进行初始化,也可以省略第一维,编译器会根据你的初始化语句自动决定第一维度。

举例:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main()
{
    char a[3][3];
    memset(a, 0, sizeof(a));
    printf("%lu\n", sizeof(a));
    for(int i=0; i<3; i++)
    {
        for(int j=0; j<10; j++)
            printf("%d ", a[i][j]);
    }

    return 0;
}

运行结果:

9
0 0 0 0 0 0 0 0 0 

扩展 4 – C99 标准的变长数组

  • 在 C99 标准之前,C 语⾔在创建数组的时候,数组大小的指定只能使用常量、常量表达式。

比如我们学过的代码:

int arr1[10];
int arr2[3+5];
  • C99 标准中存在变长数组,允许我们可以使用变量指定数组大小。

请看下⾯的代码:

int n = a+b;
int arr[n];

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

变长数组的根本特征:

就是数组长度只有运行时才能确定,所以变长数组不能初始化。它的好处是不必在开发时,随意为数组指定⼀个估计的长度,程序可以在运行时为数组分配精确的长度。

《小菜狗 C 语言入门 + 进阶笔记》目录:《小菜狗 C 语言入门 + 进阶笔记》(0)简介


每日一更!

公众号、优快云等博客:小菜狗编程笔记

谢谢点赞关注哈!目前在飞书持续优化更新~

日更较慢有需要完整笔记请私我,C/C++/数据结构-算法/单片机51-STM32-GD32-ESP32/嵌入式/Linux操作系统/uboot/Linux内核-驱动-应用/硬件入门-PCB-layout/Python/后期小程序和机器学习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小菜狗编程笔记

你的鼓励将是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值