C语言基础级——N维数组定义与使用

C语言基础级——一维数组

转载请标明出处!
大家好,我叫Home,是有关于一维数组、二维数组与多维数组的基本知识点的讲解。OK,废话不多说,我们先来了解一维数组。

一维数组

数组(array)是由一系列类型相同的元素构成的。数组的引入帮助我们更好地统一管理并处理相同类型的数据,就如同汽车上记录每天的里程数、仓库库存的清单等等。

1.1 声明

数组声明格式为:

  • [数据类型] [数组名][索引值];

它的作用就是告诉编译器,需要它去创建一个以数组下标([索引值])为长度的元素类型([数据类型]),如:

  •   int arr[10];
    

那这样编译器就会按照数组的声明要求在内存空间上,开辟出一段内存空间,且这段内存空间是连续的,例如:

#include<stdio.h>

int main()
{
    
    
    // 数组声明
    int arr[5];

    for(int i = 0; i < 5; i++)
    {
    
    
        // 打印5个数组元素的地址
        printf("arr[%d] = %p\n", i, &arr[i]);
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述

这里要注意的地方有两点:

  • 图中的内存地址是省略简写的,写全了0x1de0对应是0x7fff7a9f1de0
  • arr[索引值]是从0开始的,也就是代表arr的第一个元素。
  • arr[0] ~ arr[1] 的地址是间隔四个字节,这是因为arr数组是int类型,所以arr每个内存空间都占四个字节大小。

1.2 初始化

单单是默认有数组声明,一定是不够的。我们要初始化数组,不然我们开辟这段内存空间干啥子呢?

// 数组初始化
int arr[5] = {
    
    10, 100, 1000, 10000, 100000};

这种初始化方法是最常见的和常用的写法,形式上,我们用一对{}将数据包含起来,每个元素的数据之间以,(逗号)进行隔开,后面不要忘了加;(分号)哦。

在多提一嘴,在ANSI C 标准之前的编译器当中(旧标准),可能会报错,前面的版本还没有这种初始化的方式。

同样,我们还可以给全部元素赋相同的值。

// 数组初始化
int arr[0] = {
    
    0};

1.2.1 其它初始化

1.2.1.1 常见初始化—1
  • 第一种定义数组索引值要大于初始化个数。
// 数组初始化
int arr[5] = {
    
    10, 100};

arr数组索引值也就是长度有5,而初始化只有arr[0] = 10; arr[1] = 100;剩下的三个arr[2]~arr[4],编译器会默认给未初始化的数组空间用0来填充。我们来试一试:
在这里插入图片描述

1.2.1.1 常见初始化—2
  • 第二种定义数组索引值小于所初始化个数。
// 数组初始化
int arr[5] = {
    
    10, 100, 1000, 10000, 100000, 200000, 300000};

arr数组长度为5,而初始化个数要大于5的,这样的方式,在旧版本编译器里会直接进行报错,而新版本(gcc version 9.3.0),会报警告为超过元素初始化的范围值,因为所定义的长度(所开辟的内存空间)并不足以存放初始化值。
在这里插入图片描述

注意:

可能同学看到这边编译器不会直接报错误,而且程序还是能够正常的运行,觉得这个问题也还OK。其实,并不是这样的,程序可能会数组越界产生了不可预料的错误,这个到后面,Home会做一章节内容来详细总结。

1.2.2 数组初始化规范

回归主题,那我们如何避免出现这样的错误,这个时候就是要拥有一个好的编程规范了,有两种方法,第一种是利用宏定义规定数组长度;第二种是利用sizeof运算符计算出数组长度。两种方式,没有限定,应灵活运用。

引用宏定义
#include<stdio.h>

#define MAX_ARR     5

int main()
{
    
    
    int arr[MAX_ARR] = {
    
    10, 100, 1000, 10000, 100000};

    for(int i = 0; i < MAX_ARR; i++)
    {
    
    
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    return 0;
}

在这里插入图片描述

引用sizeof

利用sizeof运算符计算操作数的大小,可以直接计算出
数 组 的 长 度 = 数 组 所 占 的 字 节 / 单 个 数 组 所 占 的 字 节 。 数组的长度 = 数组所占的字节 / 单个数组所占的字节。 =/
计算int arr[5] = {10, 100, 1000, 10000, 100000}; 定义的数组arrint类型,我们知道int类型占4个字节,我们可以通过sizeof计算出整个数组占 20 个字节。那么可以计算出数组长度len = 5 (20 / 4),这里采用的方式是:

// 计算整个int类型数组所占的字节数
sizeof(arr);

// 计算单个int类型数组所占的字节数
sizeof(arr[0]);
// 也可以是
sizeof(int);
// 或者是
sizeof(*arr);
#include<stdio.h>

int main()
{
    
    
    // 初始化arr数组
    int arr[5] = {
    
    10, 100, 1000, 10000, 100000};
	
    // 计算出arr数组的长度
    int len = sizeof(arr) / sizeof(arr[0]);
	
    // 计算并打印整个int类型数组所占的字节数
    printf("sizeof(arr) = %ld\n", sizeof(arr));
    // 计算并打印单个int类型数组所占的字节数
    printf("sizeof(arr[0]) = %ld\n", sizeof(arr[0]));
	
    // 循环遍历数组
    for(int i = 0; i < len; i++)
    {
    
    
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    return 0;
}

在这里插入图片描述

1.3 赋值

C语言数组赋值,想要实现对数组的N个元素进行赋值操作,如果是一个int数组,我们要采用逐个输入的方式。

#include<stdio.h>


int main()
{
    
    
    int arr[5];
	
    // 逐个元素输入
    for(int i = 0; i < 5; i++)
    {
    
    
        scanf("%d", &arr[i]);
    }
    // 遍历输出元素
    for(int i = 0; i < 5; i++)
    {
    
    
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    return 0;
}

在这里插入图片描述

1.4 易错

我们本意上想要实现整体数组进行赋值,我们看下这种方式是否可行?

#include<stdio.h>

int main()
{
    
    
    int arr_old[5] = {
    
    10, 100, 1000, 10000, 100000};
    int arr_new[5];
    
    // 一. 数组名之间赋值,是错误的
    arr_new = arr_old;

    // 二. 第六个元素赋值, 也是错误的
    arr_new[5] = arr_old[5];

    // 三. 列表形式直接赋值, 也是错误的
    arr_new[5] = {
    
    10, 100, 1000, 10000, 100000};
    return 0;
}

在这里插入图片描述

  • 第一个错误,数组名之间直接赋值的方式,这种方式在C语言当中不允许的。此时的arr_oldold_new是数组名,它代表的是当前数组的首地址,数组名相当于是一个常量。常量之间赋值是无法进行赋值的。
  • arr&arr[0] 它们都是代表着数组的首地址。
// 定义单个变量
int a;
// 取a的地址
&a;
// 定义一个数组
int arr[5];

// arr就是这个数组的首地址
arr;

// &arr[0] 也是代表着数组的首地址
&arr[0];

在这里插入图片描述

  • 第二个错误,首先分为两部分:
    • arr_new[5] 数组代表的是第个元素,一共是开辟了个内存空间,不够存放元素,从而导致数组越界,是非常严重的错误。
    • 这样的方式相当于给第个元素进行赋值,并不能达到预期效果,应该使用逐个赋值方式。
    • 第二个会发现编译并没有报错,而我说这是一个很严重的错误,前后矛盾?
      • 这和编译器机制有关系的,本身编译器默认不去检测用户所使用的数组边界,如果每个数组边界都去检测,对于编译器是麻烦的,是会导致效率降低。为此,编译器对程序员的绝对信任,相信你不会犯这样错误。。。所以就有了之后的结果,每次都犯错。。。
  • 第三个错误在C语言当中是不允许。想要实现数组的赋值,除非在初始化过程中,可以直接进行赋值操作以外,其它要采用逐个赋值的方式。

二维数组

例子:公司每年都会提供给员工一次免费体检机会,体检的其中一个基本项目是身高、体重。

姓名 身高(cm) 体重(kg)
小朱 156 40
中朱 183 50
大朱 193 60

在这里,我们就可以使用二维数组保存这里面的数据。

1.1 声明与初始化

  • 声明并初始化二维数组,把三名员工的基本数据保存在二维数组当中。
// 初始化二维数组
int arr[3][2] = {
    
    {
    
    156, 40}, {
    
    183, 50}, {
    
    193, 60}};
  • 可以把内部嵌套的{}去掉。
// 初始化二维数组
int arr[3][2] = {
    
    156, 40, 183, 50, 193, 60};
  • 把行索引值去掉,留下列索引值。
// 初始化二维数组
int arr[][2] = {
    
    156, 40, 183, 50, 193, 60};
  • 也可以部分进行初始化,默认未初始化赋值的数为0
int arr[3][2] = {
    
    {
    
    156, 40}, {
    
    183, 50}, {
    
    193}};

在这里插入图片描述

2.1 内存排布

一维数组当中,我们知道一维数组在内存当中存放是连续的,我们认为二维数组也是连续的。我们按顺序打印每个元素的地址,来看看是否符合猜想的结果。

#include<stdio.h>

int main()
{
    
    
    int arr[3][2] = {
    
    {
    
    156, 40}, {
    
    183, 50}, {
    
    193, 60}};

    for(int i = 0; i < 3; i++)
    {
    
    
        for(int j = 0; j < 2; j++)
        {
    
    
            printf("arr[%d][%d] = %p\n", i, j, &arr[i][j]);
        }
    }
    return 0;
}

在这里插入图片描述

N维数组

有一维数组,二维数组,那一定会有三维数组、四维数组、五维数组…

// 初始化三维数组
int arr[2][2][2] = {
    
    1, 2, 3, 4, 5, 6, 7, 8};
// 初始化四维数组
int arr[2][2][2][2] = {
    
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};

超过二维的数组,不具有参考价值了,真正的工作当中几乎看不到,有兴趣的同学,可以自行研究~

猜你喜欢

转载自blog.csdn.net/qq_43125185/article/details/110195466