一维数组和二维数组

目录

1. 一维数组的创建和初始化

1.1 数组的创建

1.2 数组的初始化

1.3 一维数组的使用

1.4 一维数组在内存中的存储

2.二维数组的创建和初始化

2.1 二维数组的创建

2.2 二维数组的初始化

2.3 二维数组的使用

2.4 二维数组在内存中的存储

3.数组越界

4. 数组作为函数参数

4.1 冒泡排序的错误设计

4.2 数组名是什么

4.3 冒泡排序函数的正确设计


1. 一维数组的创建和初始化

1.1 数组的创建

数组是一组相同类型元素的集合

数组的创建方式:

type arr_name [const_n]
type:数组的元素类型
const_n:一个常量表达式,用来指定数组的大小

数组创建的实例:

int arr1[10];

int count = 10;
int arr2[count];

char arr3[10];
float arr4[1];
double arr5[20];

在C99标准后,[]中可以使用变量,C99标准支持了变长数组的概念。

1.2 数组的初始化

数组的初始化是指,在创建数组的同时给数组的内容一些合理的初始值(初始化)

int arr1[10] = {1,2,3};
int arr2[] = {1,2,3,4};
int arr3[5] = {1,2,3,4,5};
char arr4[3] = {'a','b','c'};
char arr5[] = "abcdef";

arr1是不完全初始化,剩余位置默认为0。

arr5中"abcdef"自动在最后会有一个\0作为结束。arr4中无'\0',后续会打印随机值。

1.3 一维数组的使用

#include <stdio.h>

int main(void)
{
    int arr[10]={0};
    int sz = sizeof(arr)/sizeof(arr[0]);//总大小除一个元素大小=元素的个数
    int i = 0;
    for(i=0;i<sz;i++)
    {
        arr[i] = i;
    }
    for (i=0;i<sz;i++)
    {
        printf("%d ",arr[i]);
    }
    return 0;
}

数组是使用下标来访问的,下标是从0开始的

数组的大小可以通过计算得到。

1.4 一维数组在内存中的存储

int main()
{
    int arr[10] = {0};
    int i = 0;
    int sz = sizeof(arr)/sizeof(arr[0]);

    for (i=0;i<sz;++i)
    {
        printf("&arr[%d] = %p\n",i,&arr[i]);
    }
    return 0;
}
&arr[0] = 0000008bd21ff9d0
&arr[1] = 0000008bd21ff9d4
&arr[2] = 0000008bd21ff9d8
&arr[3] = 0000008bd21ff9dc
&arr[4] = 0000008bd21ff9e0
&arr[5] = 0000008bd21ff9e4
&arr[6] = 0000008bd21ff9e8
&arr[7] = 0000008bd21ff9ec
&arr[8] = 0000008bd21ff9f0
&arr[9] = 0000008bd21ff9f4

我们可以看到随着数组下标的增长,元素的地址,也在有规律的递增。由此可以得出结论:数组在内存中是连续存放的。


2.二维数组的创建和初始化

2.1 二维数组的创建

int arr[3][4];
char arr[3][5];
double arr[2][4];

2.2 二维数组的初始化

int arr[3][4] = {1,2,3,4};
int arr[3][4] = {{1,2},{4,5}};数据不够,按需分组
int arr[][4] = {{2,3},{4,5}};
二维数组如果有初始化,行可以省略,列不能省略

二维数组的每一行都相当于一个一维数组,二维数组其实就等于一维数组的数组。

2.3 二维数组的使用

int main()
{
    int arr[3][4] = {0};
    int i = 0;
    for (i=0;i<3;i++)
    {
        int j = 0;
        for(j=0;j<4;j++)
        {
            arr[i][j] = i*4+j;
        }
    }
    for (i=0;i<3;i++)
    {
        int j = 0;
        for (j=0;j<4;j++)
        {
            printf("%d ",arr[i][j]);
        }
    }
    return 0;
}

注意二维数组输入时:

int main()
{
    int arr[2][2];
    for(int i=0;i<2;i++)
    {
        int j = 0;
        for(j=0;j<2;j++)
        {
            scanf("%d ",&arr[i][j]);
        }
        printf("\n");//这个不能打,否则输入格式会出问题,这个在输出时才需要换行
    }
    return 0;
}

2.4 二维数组在内存中的存储

int main()
{
    int arr[3][4];
    for(int i=0;i<3;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] = 000000d0c69ff670
 &arr[0][1] = 000000d0c69ff674
 &arr[0][2] = 000000d0c69ff678
 &arr[0][3] = 000000d0c69ff67c
 &arr[1][0] = 000000d0c69ff680
 &arr[1][1] = 000000d0c69ff684
 &arr[1][2] = 000000d0c69ff688
 &arr[1][3] = 000000d0c69ff68c
 &arr[2][0] = 000000d0c69ff690
 &arr[2][1] = 000000d0c69ff694
 &arr[2][2] = 000000d0c69ff698
 &arr[2][3] = 000000d0c69ff69c

第一行后跟着第二行地址,想象之中是成格状的,实际也是连续的。二维数组在内存中也是连续存储的。


3.数组越界

数组的下标是有范围限制的,数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1,所以如果数组的下标超过这个范围,数组就越界访问了,超出了数组合法空间的访问。

C语言本身不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员最好自己做越界检查。


4. 数组作为函数参数

往往我们在写代码时,会将数组作为参数传给函数,比如:我们要实现一个冒泡排序函数,就会将一个整形数组排序:

4.1 冒泡排序的错误设计

方法1:

#include <stdio.h>

void bubble_sort(int arr[])
{
    int i = 0;
    int sz = sizeof(arr)/sizeof(arr[0]);
    for(i=0;i<sz-1;i++)
    {
        int j = 0;
        for(j=0;j<sz-i-1;j++)
        {
            if(arr[j]>arr[j+1])
            {
                int tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            }
        }
    }
}

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

核心思路:两个相邻元素比较,在某一趟里相邻变换,走sz-1趟,一趟搞定一个数字,让他出现在该出现的位置

第一趟:10个元素,9对比较,0<=j<10-0-1=9

第二趟: 9个元素,8对比较,0<=j<10-1-1=8

...

思路是对的,但是这样写出来的代码是错的,我们可以发现调试后,bubble_sort函数内部的sz是1,我们想要的sz是10。

那为什么呢?首先我们需要了解数组名在程序中究竟是什么:

4.2 数组名是什么

#include <stdio.h>

int main(void)
{
    int arr[10] = {1,2,3,4,5};
    printf("%p\n",arr);
    printf("%p\n",&arr[0]);
    printf("%d\n",*arr);
    return 0;
}

结论:数组名是首元素地址

如果数组名是首元素地址,那么:

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

为什么输出的结果是40

补充:

1.sizeof(数组名),计算的是整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组

2.&数组名,取出的是数组的地址,此时数组名表示整个数组。

除了上述两种情况,所有的数组名都表示数组首元素地址。

#include <stdio.h>

int main(void)
{
    int arr[10] = {1,2,3,4,5};
    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;
}
0000000639fff820
0000000639fff824

0000000639fff820
0000000639fff824

0000000639fff820
0000000639fff848

其实指针+1,跳过几个字节就能很明显看出具体区别。

4.3 冒泡排序函数的正确设计

这时我们就可以回过头看冒泡排序的问题:为什么sz是1不是10

因为当数组传参时,实际上是把数组的首元素地址传过去了,所以即使在函数参数部分写成数组的形式:int arr[]表示的依然是一个指针:int *arr。

那么,函数内部sizeof(arr)的结果是4.

那么正确的方法该如何设计呢?

#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;j++)
        {
            if(arr[j]>arr[j+1])
            {
                int tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            }
        }
    }
}

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

把sz也设计到函数参数中,一起传过去,这样的传地址效率高。

注意:数组传参只写数组名,若写arr[i]则表示那个值。

二维数组的数组名=也是首元素地址=第一行的地址,arr+1表示跳过一行。


本章完

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值