【 C语言 】 数组(详细讲解+代码展示)

简介: 在程序设计中,为了方便处理数据把具有相同类型的若干变量按有序形式组织起来——称为数组。数组也是我们在学习C语言的基础之一,那对于数组的知识你掌握多少呢?那些细枝末节你会不会啊?看完这篇文章,你将掌握数组。

数组概述

在程序设计中,为了方便处理数据把具有相同类型的若干变量按有序形式组织起来——称为数组。

image.gif0$1FFRUPAVX1_U9DZ7}L4SS.png

数组就是在内存中连续的相同类型的变量空间。同一个数组所有的成员都是相同的数据类型,并且所有的成员在内存中的地址是连续的。

--我们可以将一个数组分解为多个数组元素:这些数组元素可以是基本数据类型或构造类型。

int a[100];  //基本数据类型
struct Stu p[100];    //构造

image.gif

(注:构造数据类型:数组类型、枚举类型enum、指针类型、引用类型、结构体struct、联合体union(又称联合,共用体)、class类等。)

--按数组元素类型的不同,数组可分为:数值数组、字符数组、指针数组、结构数组等类别。

int a[100] ;    //数值数组
char s[100] ;    //字符数组
char *p[100] ;    //指针数组
struct Peo n[100] ;    //结构数组

image.gif

数组元素下标的个数也称为维数,我们可以根据维数的不同,将数组分为一维数组、二维数组、三维数组...... 我们在通常情况下将一维数组以上的数组称作多维数组 。

一维数组

如何使用一维数组

一维数组的定义包括以下几点要求:

    1. 数组名字符合标识符的书写规定(数字、英文字母、下划线) 。
    2. 数组名不能与其它变量名相同 。
    3. 方括号[]中常量表达式表示数组元素的个数 。
    //方括号中的数量是其数组所包含元素,但其下标应该从0开始 。
    a[3] 包含 a[0], a[1], a[2] 三个元素
    //0, 1, 2分别表示其元素下标 。

    image.gif

    好了,你已经掌握了一维数组的使用,下面就来练习以下一位数组的创建以及赋值吧。

    代码如下;

    #include <stdio.h>
    int main()
    {
      int a[100] ;    //在这里我们定义了一个数组a,a有100个成员,每个成员都是int类型
      //没有a这个变量,a是数组的名字,但不是变量名,它是常量
      int i = 0;
      for (i = 0; i < 100; i++)
      {
        a[i] = i; //给数组赋值 让a[0] = 0, a[1] = 1 ... a[i] = i ;
      }
      //遍历数组,输出每个成员的值
      for (i = 0; i < 100; i++)
      {
        printf("%d ", a[i]);
            printf("\n");
      }
      printf("\n");
      return 0;
    }

    image.gif

    一维数组的初始化

    我们在定义数组的同时进行赋值,就叫做初始化。如果全局数组不做初始化,那么编译器将其初始赋值为零。但是局部数组如果不进行初始化,那么其内容将为随机值。

    #include <stdio.h>
    int b[5] ;  //定义全局数组 
    int main()
    {
      int a[5] = { 1, 2, 3, 4, 5 }  ; //定义一个数组并进行初始化 
      int c[5] ;  //定义局部数组
      int d[] = {1, 2, 3} ; //如果[]中不定义元素个数,定义时必须初始化 
      int e[5] = { 0 };//所有的成员都设置为0
      printf("%d\n", a[0]) ;
      printf("%d\n", b[0]) ;
      printf("%d\n", b[2]) ;
      printf("%d\n", c[0]) ;
      printf("%d\n", c[2]) ;
      return 0;
    }

    image.gif

    运行结果:

    851YGD(9@7DT$V@6DK{D7OF.png

    这个运行结果就很好的展示了我们上面所讲到的知识。

    数组名

    一个数组方括号外的就是该数组的数组名,例如;a[10] , a就是该数组的数组名。数组名是一个地址的常量,代表数组中首元素的地址。

    下面案例中我们会用到一个函数,那么在这里我们就提前为大家普及一下,sizeof()函数。

    sizeof 是一个关键字,它是一个编译时运算符,用于判断变量或数据类型的字节大小。sizeof 运算符可用于获取类、结构、共用体和其他用户自定义数据类型的大小。

    sizeof的语法:

    sizeof (object) ;

    image.gif

    其中object是我们要去计算大小的数据类型,这些数据类型包括类、结构、共用体和其他用户自定义数据类型。

    好了,了解那么多之后我们就看一下这串代码吧。

    #include <stdio.h>
    int main()
    {
      int a[5] = { 1, 2, 3, 4, 5 }; //定义一个数组并初始化所有成员变量 
      printf("%p\n", a) ;   //数组名是一个地址的常量,代表数组中首元素的地址。 
      printf("%p\n", &a[0]) ; // a和a[0]的地址是相同的 
      int n = sizeof(a) ;     //数组占用内存的大小,5个int类型,5 * 4  = 20
      int n0 = sizeof(a[0]) ;   //数组第0个元素占用内存大小也就是这个数组中每个元素内存的大小。 
      int i = 0;
      for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)  // sizeof(a) / sizeof(a[0]就是数组中元素个数
      {
        printf("%d ", a[i]);
      }
      printf("\n");
      return 0;
    }

    image.gif

    输出结果:

    image.gif`FO%GTD~8WQ%6[BSAUBVVQU.png

    根据输出结果我们就能证实: 数组名代表数组中首元素的地址。

    代码中出现的( sizeof(a) / sizeof(a[0] )这个就是利用整个数组的大小与数组中单个元素的大小相除就可以得到我们数组中的元素了 。

    一维字符教组

    一维字符数组一般用于存储和表示一个字符串,在 C 语言中,一般以空字符 '0' (ASCII 值为 0)作为字符串结束的标志。

    一维字符教组的定义

    一维字符数组定义与一维数组相差不大,其格式为:

    char 数组名[数组大小] ;

    image.gif

    例如:

    char a[10] ;

    image.gif

    该语句定义了一个一维字符数组 a,大小为 10,即占 10 个字符变量空间,最大可存储长度为 9 的字符串(第 10 个字符为 '\0')。由于没有显式给每个字符变量赋值,故每个字符变量为随机值

    一维字符数组初始化

    以为字符数组在初始化时可以采用单个字符逐个赋值的方式初始化,也可以使用字符串初始化的方式。

    1)逐个字符赋值

    我们在逐个字符赋值的时候可能会出现以下三种情况。

    a.当字符个数少于数组空间大小时

    比如:

    char a[8] = {'h','e','l','l','o'} ;    //始值个数5小于数组a空间个数8

    image.gif

    该语句定义了含 8 个字符变量的一维字符数组a,前 5 个字符变量分别显式初始化为 'h','e','l','l','o' 等 5 个字符,后 3 个字符变量为空字符 '\0'。

    image.png

    当字符数组中含有字符串结束字符 '\0' 时,这时我们就可以使用 printf 函数和格式控制符 %s,输出该字符数组中的字符串 。其中格式控制符 %s,从输出列表中的该地址开始,到第一次遇到 '\0' 为止,这之间的字符全部输出。

    printf ("%s”,a) ;

    image.gif

    并且我们在进行一维数组初始化时,其第一维大小可以省略 。

    char a[] = {'h','e','l','l','o'} ;

    image.gif

    而这时储存的形式就变成了这样:

    image.gifimage.png

    这时数组中就不存在\0了,也就是说我们不可以使用不能使用%s去进行输出了。 所以char a[8] = {'h','e','l','l','o'}  与 char a[] = {'h','e','l','l','o'} 并不是等价关系。

    b.当字符个数等于数组空间大小时

    比如:

    char a[5]={'h','e','l','l','o'} ;    //初值个数5等于数组大小5

    image.gif

    此语句的执行结果如下图:

    image.png

    可以发现,这时char a[5]={'h','e','l','l','o'} 与 char a[] = {'h','e','l','l','o'}就成为了等价关系。同样该字符数组中不包含字符串结束标志 '\0',故不能使用 printf("%s",c); 输出其中的字符串。

    所以对于这种数组,我们通常会采用for循环的形式去输出该字符数组。

    int i;
    for(i=0;i<5;i++) 
    {
        printf ("%c",a[i]) ;
    }

    image.gif

    c.当字符个数多于空间大小时

    我们在初始化的时候,当字符个数多于空间大小时,编译时报错。

    char a[4] = {'h','e','l','l','o'}; //初值个数5大于数组大小4


    2)字符串初始化赋值

    在 C 语言中,字符串一般是指含有字符串结束符 '\0' 的若干个字符的集合。而使用双引号括起来的字符串常量,默认隐含字符串结束符 '\0'。

    char a[15] = {"Hello World"}; //注意该数组大小应足够大 不然会报错

    image.gif

    并且我们在用字符串对字符数组初始化时,一般大括号可以去掉,即:

    char a[15] = "Hello World"  ;

    image.gif

    这时我们的语句的等价关系就是:

    char a[15] = {"Hello World"} ;
    //等价于 
    char a[15] = "Hello World"  ; 
    //等价于
    char a[15]= {'H','e','l','l','o',' ','W','o','e','l','d','\0','\0','\0','\0'} ; 
    //等价于
    char a[15]= {'H','e','l','l','o',' ','W','o','e','l','d','\0'} ;
    //等价于
    char a[15]= {'H','e','l','l','o',' ','W','o','e','l','d'} ;

    image.gif

    其中,其数组大小都是我们指定的15 。 而储存形式也如下图所示。

    $@S4}J(FO3%1ECS~L%5IMBM.png

    为了防止字符个数超出数组空间的现象,我们在采用字符串对字符数组进行初始化时,一般省略一维数组空间的大小,即:

    char a[] = {"Hello World"} ;

    image.gif

    该数组中除了存储字符串中的 11 个有效字符外,还自动在字符串的结尾存储 '\0' 字符。所以该数组的大小为 12。其存储形式如下所示。

    image.png

    为节省空间以及书写方便防止不必要的失误,当我们采用用字符串对字符数组初始化时,一般均省略其一维的大小。

    一维字符数组的引用

    字符数组中的每一个元素都是一个字符,所以我们可以使用下标的形式来访问数组中的每一个字符。

    char a[]="abcd" ;

    image.gif

    定义了一个一维字符数组 a,用字符串常量对其初始化,该a数组大小为 5,前 4 个元素的值分别为 'a'、'b'、'c'、'd',第 5 个元素的值为 '\0'。其存储形式如下所示。

    image.png

    我们可以使用 a[i] 引用该数组中的每个元素,例如:

    int i;
    for(i=0;c[i]!='\0';i++) //当前i号位置的字符变量只要不是结束符就输出
        printf("%c",a[i]) ;

    image.gif

    其中for循环使用 '\0' 作为判断依据也可以很好的避免了计算长度所带来的复杂程度。

    二维数组

    当数组元素具有两个下标时, 该数组称为二维数组。 二维谁可以看做具有行和列的平面数据结构。

    如何使用二维数组

    二维数组定义的一般形式为:

    类型说明符 数组名[常量表达式1][常量表达式2]

    image.gif

    其中常量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度。

    其命名规则与一维数组的命名规则相同。比如:

    int a[3][4] ;

    image.gif

    第一行表示定义了一个 3×4,即 3 行 4 列总共有 12 个元素的数组 a。这 12 个元素的名字依次是:a[0][0]、a[0][1]、a[0][2]、a[0][3];a[1][0]、a[1][1]、a[1][2]、a[1][3];a[2][0]、a[2][1]、a[2][2]、a[2][3];

    image.gifILH(FVT@_[7_ZT2{P8EDGVF.png

    与一维数组相同,二维数组的行序号和列序号的下标都是从 0 开始的。元素 a[i][j] 表示第 i+1 行、第 j+1 列的元素。数组 int a[m][n] 最大范围处的元素是 a[m–1][n–1]。所以我们在日常引用数组元素时应该注意,下标值应在定义的数组大小的范围内。

    在C语言中,二维数组进行存放时,是按行进行存放的,就例如上文中的数组a,就是先存放a[0]行,再存放a[1]行、a[2]行,并且每行有四个元素,也是从0-3依次存放的。

    对于二维数组而言,其只是在概念上是二维的:其下标在两个方向上变化,所以对其访问一般需要两个下标。

    实际上在内存中并不存在二维数组,二维数组实际的硬件存储器是连续编址的,也就是说内存中只有一维数组,在放完一行元素之后顺次放入第二行元素,与一维数组的存放方式相同其内存地址依旧是连续的。

    下面我们来看下这串代码:

    要求,创建一个3×4的数组,并按照数组存放方式去从1开始依次递增赋值。

    #include <stdio.h>
    int main()
    {
      int a[3][4];  //定义二维数组 
      /*
      这个二维数组由三个一维数组组成
      这三个一维数组的数组名分别是 a[0],a[1],a[2] 
      而这个一维数组是 int [4]
      */ 
      int i = 0;
      int j = 0;
      int num = 0;
      for (i = 0; i < 3; i++)   //for循环给数组每个元素赋值
      {
        for (j = 0; j < 4; j++)
        {
          a[i][j] = num++;
        }
      }
      for (i = 0; i < 3; i++)
      {
        for (j = 0; j < 4; j++)
        {
          printf("%d   ", a[i][j]) ;  //输出每个成员的值 
        }
        printf("\n");
      }
      return 0;
    }

    image.gif

    运行结果:

    image.gifZYJKN~JZV`I{DVCDV818W4S.png

    二维数组的初始化

    二维数组初始化的形式为:

    数据类型 数组名[常量表达式1][常量表达式2] = { 初始化数据 } ;

    image.gif

    我们可以用下面的方法对二维数组进行初始化。

    1)按行进行初始化

    在 { } 内部再按照行数用 {}二次进行分开去初始化数组。其中行与行之间的{}要用逗号隔开;代码中{1,2,3,4}为第一行数组初始化;{5,6,7,8}为第二行数组初始化;{9,10,11,12}为第三行数组初始化。

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

    image.gif

    2)连续赋值初始化

    连续赋值就是将 { } 中的数据依次赋值给数组中的各元素。其赋值顺序与二维数组存放顺序是相同的。就比如a[3][4]数组初始化,先从a[0]行a[0][0]元素开始到a[0][3]元素之后再到a[1]行a[1][0]元素开始.......最终到a[2][3]为止。

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

    image.gif

    3)部分元素初始化

    我们在对二维数组进行赋值的时候可以只给数组中部分元素赋初值,那么未初始化的元素则为0。

    int a[3][4] = { 1, 2, 3, 4  } ;
    int a[3][4] = {{1, 2}, {5}, {9}};

    image.gif

    第一行是只对二维数组的第一行进行初始化,其余行则为0。

    第二行是对第一行的前两个元素赋值、第二行和第三行的第一个元素赋值。其余元素自动为 0。

    上面两种情况初始化后的结果分别是:

    image.png

    4)全员赋0初始化

    二维数组“清零”,也就是将里面每一个元素都赋值为零,这时就不需要跟之前一样将每个元素都赋值一遍了。

    int a[3][4]={0};

    image.gif

    5)第一维不定义初始化

    如果我们在定义的时候对所有的元素都赋初值的话,那么我们可以不指定第一维的长度,但第二维的长度不能省。这时系统会根据数据总数和第二维的长度算出第一维的长度。但这种省略的写法几乎不用,因为可读性差。

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

    image.gif

    所以二维数组的初始化方式总结下来也就以下几种了:

    //按行进行初始化   
        int a[3][4] = {{ 1, 2, 3, 4 },{ 5, 6, 7, 8, },{ 9, 10, 11, 12 }} ;
      //连续赋值初始化
      int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12  } ;
      //部分元素初始化
      int a[3][4] = { 1, 2, 3, 4  } ;
      //全员赋0初始化
      int a[3][4] = {0} ;
      //第一维不定义初始化
      int a[][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

    image.gif

    数组名

    和一维数组一样,数组名是一个地址的常量,代表数组中首元素的地址

    #include <stdio.h>
    int main()
    {
      //定义了一个二维数组
      int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12  } ;
      //数组名为数组首元素地址,二维数组的第0个元素为一维数组
      printf("a = %p\n", a) ;    //输出地址
      printf("a[0] = %p\n", a[0]) ;
      //测二维数组所占内存空间
      //sizeof(a) = 3 * 4 * 4 = 48
      printf("sizeof(a) = %d\n", sizeof(a)) ;
      //测第0个元素所占内存空间
      printf("sizeof(a[0]) = %d\n", sizeof(a[0]) ) ;
      //测第0行0列元素所占内存空间
      printf("sizeof(a[0][0]) = %d\n", sizeof(a[0][0])) ;
      return 0;
    }

    image.gif

    运行结果:

    A2BW]2ASX~A[)TE(997}PP2.png

    通过运行结果我们可以看出,数组名a的地址也就是二维数组中a[0][0]的地址。以及每个int元素所占内存是4,如果要测量每行所占内存只需输入一个下标即可。

    于是我们就可以用内存大小的比值去计算出二维数组行数和列数。具体计算操作如下:

    //求二维数组行数
      printf("i = %d\n", sizeof(a) / sizeof(a[0])) ;
      // 求二维数组列数
      printf("j = %d\n", sizeof(a[0]) / sizeof(a[0][0])) ;
      //求二维数组行*列总数
      printf("n = %d\n", sizeof(a) / sizeof(a[0][0])) ;

    image.gif

    二维字符数组

    二维字符数组是用来存放字符串, 二维字符数组每一行可以看做一维字符数组, 即二维字符数组的每一行可以存放一个字符串。

    二维字符数组的定义

    二维字符数组的定义与一般二维数组的定义方式相同,只是要使用char数据类型去进行定义。

    char 数组名[第一维大小][第二维大小];

    image.gif

    例如:

    char a[3][4] ;

    image.gif

    二维字符数组 a 有3行4列, 每一行可以存放长度小于或等于3的字符串(原因:不要忘记要给字符串结束标识符留一个位置)。

    二维字符数组的初始化

    其与二维数组的初始化方式大致相同,其也可以在定义时初始化。通常情况下,二维数组的每一行分别使用一个字符串进行初始化。

    char a[2][6] = {"Hello","C++" } ;
    char a[][6] = {"Hello","C++"} ;//第二维同样不可省略
    //等价于
    char a[2][6] = {{"Hello"},{"C++"} } ;

    image.gif

    以上三条初始化语句中以及等价关系中,二维数组的第一维大小均可省略。数组 a 的逻辑结构如下所示:

    image.gifimage.png

    a由于在二维字符数组每个字符串单独占一行, 所以可以用 a[n] 引用该二维数组字符中第 n 行的字符串;也可以用 a[i][j] 引用某行某列中的单独一个字符串 。

    二维字符教组的引用

    在平时使用中,可以使用行下标和列下标引用二维字符数组中的每个元素(字符),例如:

    printf ("%c",a[1][4]) ;     //输出1行4列元素
    scanf ("%c",&a[2][3]) ;     //输入一个字符到2行3列元素中
    a[2][0]='B' ;     //把字符赋值给2行0列元素
    printf ("%s",a[1]) ;     //c[1]为第2行的数组名(首元素地址)
    scanf ("%s",a[2]) ;     //输入字符串到c[2]行,从c[2]行的首地址开始存放

    image.gif

    下面这些是我们在使用时经常与遇见的错误,这里我给大家列出来了几条:

    a[0][0]="A" ;     //行、列下标表示的为字符型元素,不能使用字符串赋值
    printf ("%c",a[2]) ;      //a[2]为第3行的首地址,不是字符元素,故不能用%c

    image.gif

    多维数组

    多维数组我们平时见到的其实并不多,所以在这里并不做强制要求,如果感兴趣的朋友可以了解一下。

    多维数组的定义与二维数组类似,其语法格式具体如下:

    数组类型修饰符 数组名 [n1][n2]…[nn];

    image.gif

    int a[3][4][5] ;  //定义三维数组 
    int b[4][6][7][3] ;   //定义四维数组

    image.gif

    这里我们拿三维数组来进行举例讲解:

    其数组的名字是a,数组的长度为3,每个数组的元素又是一个二维数组,这个二维数组的长度是4,并且这个二维数组中的每个元素又是一个一维数组,这个一维数组的长度是5,元素类型是int。

    #include <stdio.h>
    int main()
    {
      //定义三维数组 
      int a[3][4][5] = 
        { { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, { 0 }, { 0 } }, { { 0 }, { 0 }, { 0 }, { 0 } }, { { 0 }, { 0 }, { 0 }, { 0 } } };
      int i, j, k;
      for (i = 0; i < 3; i++)
      {
        for (j = 0; j < 4; j++)
        {
          for (k = 0; k < 5; k++)
          {
            printf("%d, ", a[i][j][k]); //输出 
          }
          printf("\n");
        }
      }
      return 0;
    }

    image.gif

    结尾

    好啦!到这里数组的基本知识也所剩无几了,如果还有什么不理解的地方可以在下方评论区随便留言,我看到后会及时的回复,当然只学这些数组知识是仅仅不够的,接下来我会出一期数组的实战强训练用于加强对本篇数组知识的学习以及强化一些深度知识,在那里就没有太多的知识点了,我会将一些数组中常见的题目(逆序,排序等),以及与数组有关的一些代码操作给大家讲解并附上代码供大家学习参考。如果大家感兴趣还请关注一下本人,本人会不定期出一些知识讲解博文,万分感谢。

    目录
    相关文章
    |
    27天前
    |
    存储 编译器 C语言
    【数据结构】C语言实现链队列(附完整运行代码)
    【数据结构】C语言实现链队列(附完整运行代码)
    36 0
    |
    27天前
    |
    存储 算法 程序员
    【数据结构】C语言实现顺序表万字详解(附完整运行代码)
    【数据结构】C语言实现顺序表万字详解(附完整运行代码)
    39 0
    |
    1月前
    |
    C语言
    在C语言中数组作为函数参数的应用与示例
    在C语言中数组作为函数参数的应用与示例
    15 0
    |
    1月前
    |
    算法 安全 C语言
    使用C语言实现DES算法代码
    使用C语言实现DES算法代码
    |
    1天前
    |
    存储 算法 C语言
    C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
    C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
    |
    3天前
    |
    存储 C语言
    C语言中字符串的引用与数组元素操作
    C语言中字符串的引用与数组元素操作
    12 0
    |
    23天前
    费马螺线在现实生活中的应用
    费马螺线在现实生活中的应用
    10 1
    |
    23天前
    |
    编译器 程序员 C语言
    【C语言】变长数组,二分查找和数组之间自动替换的实现
    【C语言】变长数组,二分查找和数组之间自动替换的实现
    |
    23天前
    |
    存储 C语言
    【C语言数组】创建、初始化、以及使用2
    【C语言数组】创建、初始化、以及使用
    |
    1月前
    |
    存储 机器学习/深度学习 算法
    C语言代码实现数据结构与算法
    以上代码中,哈希表使用链表解决哈希冲突,每个链表节点包含一个键值对。hash函数用于计算键值对应的哈希值,insert函数用于向哈希表中插入一个键值对,若当前位置为空,则直接插入;否则,将新节点插入到链表末尾。search函数用于在哈希表中查找指定键值的值,若存在则返回其值,否则返回-1。
    32 1