C语言——数组 (详解!!!)

@toc数组

1、一维数组

一、一维数组的建立

数组是一组相同类型元素的集合。 数组的建立方式:markdown

type_t   arr_name   [const_n];
//type_t 是指数组的元素类型
//arr_name是数组名
//const_n 是一个常量表达式,用来指定数组的大小

数组建立,[ ]必须为常量表达式。ide

//数组类型  数组名  [数组元素个数] = {初始化}
    int     arr     [10]        = { 0 };

二、一维数组的初始化

数组的初始化是指,在建立数组的同时给数组的内容一些合理初始值。函数

int arr1[10] = { 1,2,3 }; //不彻底初始化
int arr2[] = { 1,2,3,4 };
int arr3[5] = { 1,2,3,4,5 };  //彻底初始化
char arr4[3] = {'a ', 98,'c'};
char arr5[] = { 'a ', ' b', 'c ' };
char arr6[] = "abcdef";

图片.png

这里咱们要注意区分优化

char ch2[] = { 'b','i','t' };
char ch4[] = "bit";

图片.png

1,strlen 和 sizeof没有什么关联3d

2.strlen 是求字符串长度的-只能针对字符串求长度 ,求字符串的长度-'\0'以前的字符个数- -库函数 - 使用得引头文件code

3.sizeof 计算变量、数组、类型的大小 - 单位是字节 - 操做符blog


三、一维数组的使用

对于数组的使用咱们以前介绍了一个操做符︰[ ]下标引用操做符。它其实就数组访问的操做符。咱们来看代码︰排序

#include <stdio.h>

//打印数组
int main()
{
    int arr[10] = { 0 };//数组的不彻底初始化
       //计算数组的元素个数
    int sz = sizeof(arr) / sizeof(arr[0]);
    //对数组内容赋值,数组是使用下标来访问的,下标从0开始。因此:
    int i = 0;//作下标

    //输出数组的内容
    for (i = 0; i < sz; ++i)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}  

//打印字符串
int main()
{
    char arr[] = "abcdef";//[a][b][c][d][e][f][\0]
    //printf("%c\n", arr[3]);
    int i = 0;
    int len = strlen(arr);
    for(i=0; i<len; i++)
    {
        printf("%c ", arr[i]);
    }

图片.png

四、一维数组在内存中的存储

接下来咱们探讨数组在内存中的存储。看下图︰three

//打印地址
#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    int i = 0;
    for (i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
    {
        printf("&arr[%d] = %p\n", i, &arr[i]);
    }
    return 0;
}

图片.png

经过观察咱们能够看到:
1.一维数组在内存中是连续存放的
2.随着数组下标的增加,地址是由低到高变化的

图片.png

数组地址连续存放有什么实际意义或做用吗?
看下面这个例子:

图片.png

这个例子能够很好说明刚刚的问题,正由于数组是连续存放的,经过数组首元素的地址日后找能够找到每个数组对应的元素!


2、二维数组

一、二维数组的建立

二维数组的使用也是经过下标的方式。几行几列,看代码:

//二维数组建立
int arr1[3][4];
char arr2[3][4];
double arr3[4][5];

当咱们建立一个二维数组int arr[3] [4] 后,在咱们的脑海中要形式对应的三行四列二维数组,这个数组的每一个元素都是int类型。(二维数组有行列之分)

图片.png


二、二维数组的初始化

//初始化---建立的同时给赋值
方式①
int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };//彻底初始化

图片.png

三行四列的数组arr1有12个元素,咱们能够在初始化的时候直接用大括号将12个元素括起来赋值给arr1。

int arr[3][4]={1,2,3.{4,5,67,8,9}}

思考:若是说咱们给的元素个数大于12个或小于12个会发生什么呢?

(1)大于12个元素时:程序运行时,编译报错,没法正常执行,报错缘由就是初始值设定项太多。从这个报错咱们大概就能知道对于数组建立,编译器会检查初始项是否大于数组的长度。这个其实也很好理解,数组本质也是变量,变量的建立须要向内存申请空间,若是初始值设定项占用的空间大于数组申请的空间,就会“越界访问”(好比数组申请一块40个字节大小的空间,咱们在设置初始项的时候给了11个int类型的值,也就是44个字节,超过数组自己能够访问空间的大小了)。

图片.png

(2)小于12个元素时:数组会进行不彻底初始化,前几项会赋值成初始化的值,后面几项会自动赋初始值0填充。(若是是字符数组,会用\0填充,\0的ASCII码值是0)

图片.png


二维数组能够看做由多个一维数组组成,因此咱们能够这样初始化:

int arr1[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} }

图片.png

(1)在使用方式②初始化int arr1[3][4]时,若是初始的行或者列超限会怎么样呢?
能够看到会出现相似方式①越界的问题。这个其实也比较好理解,int arr[3][4],二维数组arr是一个三行四列的形式,其实将二维数组arr[3][4]看做有三个一位数组arr[4]组成的数组,这个数组有三个元素,每一个元素都是由四个元素组成的数组。若是初始的行或者列超限就会致使相似方式①说到的越界访问的问题,因此报错编译不过去!
图片.png

(2)初始化的时候不彻底赋值会怎么样呢?
数组会进行不彻底初始化,前几项会赋值成初始化的值,后面几项会自动赋初始值0填充。(若是是字符数组,会用\0填充,\0的ASCII码值是0)

图片.png

注意:二维数组在建立的时候**行能够省略,列不能省略**(第一个[ ]中的值能够不写,第二个[ ]值必须写)


三、二维数组的使用

二维数组的使用方式也是经过下标的方式。二维数组的行和列下标都是从0开始的:

图片.png

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

图片.png


四、二维数组在内存中的存储

以前咱们提到在建立二维数组的时候,会在脑海中将其想象成一个二维表(有行和列),可是二维数组在内存的存储形式是什么样的呢?跟咱们刚刚想的二维表形式同样存储方式吗?
为了研究二维数组的存储,这里咱们能够尝试将二维数组的每一个元素都打印出来:

图片.png

咱们能够看到二维数组的每一个元素之间均相差4个字节,因此二维数组在内存中也是连续存放的!这个连续存放有两层含义:1.每一行内部的元素连续存放 2.行与行之间连续存放

图片.png

图片.png


思考:了解二维数组在内存中是连续存放的有什么做用?
一、在说int arr[][4]; 这个例子的时候,咱们说二维数组行能够省略,列不能够省略。为何列不能够省略呢?
当列肯定的时候,咱们才能知道一行有多少个元素,才能知道第二行从哪里开始,不然的话,这个二维数组就不是肯定的
二、只有当二维数组是连续存放的方式,当我拿到二维数组首元素地址的时候,就能够依次访问到这个数组的全部元素。

图片.png

二维数组arr[3] [4]能够看做由三个一维数组组成,这三个一位数组的数组名分别为arr[0], arr[1], arr[2]

图片.png

数组经过数组名 + [下标]访问其成员,由此咱们可知arr[0], arr[1], arr[2]也是数组名。
该二维数组的数组名:arr
二维数组的第一行数组名:arr[0]
二维数组的第二行数组名:arr[1]
二维数组的第三行数组名:arr[2]

3、数组做为函数参数

冒泡排序思想

咱们在写代码的时候,每每会将数组做为参数传给函数,好比:咱们要实现一个冒泡排序函数将一个整型数组排序。那咱们将这样使用函数:

冒泡排序的思想:两两相邻的元素进行比较,而且可能的话须要交换。

图片.png

一趟解决一个数字的排序问题,第一趟最大值9出现到最右侧,第二趟8到右侧第二位
10个数字,须要进行9躺冒泡排序
n个数字,须要进行 n - 1躺

一趟冒泡排序内部:
第一趟:10个数字待排序,9对比较
第二趟:9个数字待排序,8对比较
第三趟:8个数字待排序,7对比较
……
第九趟:2个数字待排序,1对比较

代码实现:

#include<stdio.h>

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

void bubble_sort(int arr[], int sz)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < sz - 1; i++)//肯定冒泡排序的趟数
    {
        for (j = 0; j < sz - 1 - i; j++)//肯定每一趟两两比较的次数
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

int main()
{
    int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    printf("排序前:");
    print(arr, sz);

    //冒泡排序,进行升序排列
    bubble_sort(arr, sz);
    printf("排序后:");
    print(arr, sz);

    return 0;
}

图片.png

冒泡排序法优化:当咱们要排序的数组自己就是有序的时候,或者说数组排序的时候排好前几项就已经有序了,这时候按照上面的方法还要进行两两比较,效率就比较低,对此咱们进行想要的优化。

#include<stdio.h>

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

void bubble_sort(int arr[], int sz)
{
    int i = 0;
    int j = 0;

    for (i = 0; i < sz - 1; i++)//肯定冒泡排序的趟数
    {
        int flag = 1;//用于判断比较是否继续
        for (j = 0; j < sz - 1 - i; j++)//肯定每一趟两两比较的次数
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                flag = 0;
            }
        }
        if (flag == 1)
        {
            break;
        }
    }
}

int main()
{
    int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    printf("排序前:");
    print(arr, sz);

    //冒泡排序,进行升序排列
    bubble_sort(arr, sz);
    printf("排序后:");
    print(arr, sz);
    return 0;
}

4、思考:数组名究竟是什么?

咱们常常会说数组名是首元素地址,那么这个说法对不对呢?这里咱们能够验证一下。

图片.png

能够看到二者同样,因此数组名是首元素地址。

可是要排除如下两种状况:
①sizeof(数组名)-- - 数组名表示整个数组-- - 计算的是整个数组的大小单位是字节
② &数组名-- - 数组名表示整个数组-- - 取出的是整个数组的地址
除上述两种状况之外,其他状况数组名均表示首元素地址!


数组地址和数组首元素地址有什么区别?

二者的地址值是同样的,可是含义和使用不一样。

数组地址 + 1表示跳过整个数组,首元素地址 + 1表示跳到第二个元素。

图片.png
& arr + 1 与 & arr 相差40个字节
arr + 1 与 arr相差4个字节


三维数组及多维数组

三维数组具备高、宽、深的概念,或者说行、列、层的概念,即数组嵌套数组达到三维及其以上。是最多见的多维数组,因为其能够用来描述三维空间中的位置或状态而被普遍使用。
三维数组就是维度为三的数组,能够认为表示对该数组存储的内容使用了三个独立参量去描述,但更多的是认为该数组的下标是由三个不一样的参量组成的。三维数组又被认为是二维数组的数组,而二维数组也能够认为是一维数组的数组。
数组这一律念主要用在编写程序当中,和数学中的向量、矩阵等概念有必定的差异,主要表如今数组内的元素能够是任意的相同数据类型,包括向量和矩阵。
对数组的访问通常是经过下标进行的。在三维数组中,数组的下标是由三个数字构成的,经过这三个数字组成的下标对数组的内容进行访问。
多维数组 三维或者三维以上的数组。
定义方式:type name[size1][size2]…[sizeN];
例如,下面的声明建立了一个三维 4 . 3 . 2 整型数组:
int threedim[4][3][2];
图片.png

相关文章
相关标签/搜索