五.数组(2)

5.二维数组的使用

二维数组的使用也是通过下标的方式

现创建一个三行四列的二维数组:int arr[3][4];

我们可以想象它是这样存放的

-- -- -- -- -- -- -- -- -- -- --

打印数组中所有元素

#include <stdio.h>
int main()
{
    int arr[4][4] = { {1,2,3},{4,5} };
    //用两个for循环完成对每一个下标的寻找——遍历
    int i = 0;
    for (i = 0; i < 4; i++)
    {
        int j = 0;
        for (j = 0; j < 4; j++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");//打印完一行就换行
    }
    return 0;
}

运行结果:

1 2 3 0

4 5 0 0

0 0 0 0

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

6.二维数组在内存中的存储

先来看看二维数组的各个元素在内存中是怎么存放的

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

运行结果:

&arr[0][0]=000000896D2FF4C0

&arr[0][1]=000000896D2FF4C4

&arr[0][2]=000000896D2FF4C8

&arr[0][3]=000000896D2FF4CC

&arr[1][0]=000000896D2FF4D0

&arr[1][1]=000000896D2FF4D4

&arr[1][2]=000000896D2FF4D8

&arr[1][3]=000000896D2FF4DC

&arr[2][0]=000000896D2FF4E0

&arr[2][1]=000000896D2FF4E4

&arr[2][2]=000000896D2FF4E8

&arr[2][3]=000000896D2FF4EC

&arr[3][0]=000000896D2FF4F0

&arr[3][1]=000000896D2FF4F4

&arr[3][2]=000000896D2FF4F8

&arr[3][3]=000000896D2FF4FC

观察发现:表示地址的十六进制数之间都差4(因为是整型数组),说明二维数组在内存中的存放并不是这样的:

而是像一维数组那样是连续存放的:

把arr[i][j]看成:一个叫做arr[i]的一维数组,长度是j

其中arr[i]是数组名,j是下标

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

7.数组作为函数参数

往往我们在写代码的时候,会将数组作为参数传给函数,比如我们要实现一个冒泡排序函数,将一个整型数组进行排序

冒泡排序(升序)

基本排序方法:那两个数进行比较,大的数在前就交换位置放在后面(相邻)

10个元素,最多排9趟

第一次9对元素进行比较

第二次8对,第三次7对...

下面开始实现

#include <stdio.h>
void bubble_sort(int arr[], int sz)
{
    //确定冒泡排序的趟数
    int i = 0;
    for (i = 0; i < sz - 1; i++)
    {
        //每一趟冒泡排序
        int j = 0;
        for (j = 0; j < sz - 1 - i; j++)//用sz-1-i的方式实现,一趟冒泡排序中“交换的最大次数会越来越少”
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];//前后交换
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}
int main()
{
    int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);//之前已经讲过为什么要在main函数里面算sz
    //因为数组名是首元素地址,计算后大小是4/8
    //对arr进行排序,排成升序
    //arr是数组,我们对arr进行传参时,实际上传递过去的是数组arr的首元素地址:&arr[0]
    bubble_sort(arr, sz);
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

运行结果:1 2 3 4 5 6 7 8 9 10

-- -- -- -- -- -- -- -- -- --

数组名

数组名是什么

数组名真的是首元素地址而不是别的什么东西吗,可以写一段代码来看看

#include <stdio.h>
int main()
{
    int arr[] = { 1,2,3,4,5 };
    printf("%p\n", arr);//数组名
    printf("%p\n", &arr[0]);//把数组中下标为0的元素的地址取出来,即首元素地址
    printf("%d\n", *arr);//对数组名解引用
    printf("%d\n", arr[0]);//打印一下首元素
    return 0;
}

运行结果(例):

000000B2C4B2F548

000000B2C4B2F548

1

1

-- -- -- -- -- -- -- -- -- --

结论

数组名是首元素地址

但是有两个例外

  1. sizeof(数组名)——数组名代表整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节

  1. &数组名,数组代表整个数组,&数组名,取出的是整个数组的地址

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

运行结果(例):

00000045BC4FFAD8

00000045BC4FFADC

00000045BC4FFAD8

00000045BC4FFADC

00000045BC4FFAD8

00000045BC4FFAF4

由此可见,&数组名和首元素地址的值是一样的,但是意义却完全不同,&数组名+1跳过一整个数组,数组名+1跳过一个元素

-- -- -- -- -- -- -- -- -- --

数组越界

数组的下标是有范围限制的

  • 数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就i是n-1

  • 所有数组的下标如果小于0,或者打印n-1,就是数组越界了,超出了数组可以合法访问的空间

  • C语言本身是不做数组下标的越界检查,编译器也不一定会报错。但是编译器不报错,并不是意味着程序本身就是完全正确的

  • 所以程序员写代码时,最好自己做越界的检查

#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 < 12; i++)
    {
        printf("%d\n", arr[i]);
    }
    return 0;
}

运行结果:

1

2

3

4

5

6

7

8

9

10

-858993460

-858993460

本长老的编译器(vs2022)是没有报错哒,但是有一个警告:

严重性代码说明项目文件行禁止显示状态

警告C6201索引“11”超出了“0”至“9”的有效范围(对于可能在堆栈中分配的缓冲区“arr”

说明确实可以做到越界访问而不报错,但是编译器也会提示你,这里可能出错了

所以我们应当自己做好越界的检查

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

世长老

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值