【C语言】数组

一、什么是数组

数组:一些具有相同类型的数据的集合

二、一维数组的定义和初始化

1.一维数组定义的一般形式: 类型名 数组名 [ 数组长度 ]
(1)类型名:指定数组中每个元素的类型

(2)数组名:数组变量的名称,是一个合法的标识符

(3)数组长度:一个整型常量表达式,设定数组的大小

如:int a[10];//定义了一个有10个整型元素的数组a

2.一维数组的初始化:
(1)其一般形式为:类型名 数组名 [ 数组长度 ] = { 初值表 } ; 如:int arr[10] = {1,2,3,4,5,6,7,8,9,10};
(2)自动计算数组的长度:如果声明中未给出数组的长度,编译器会把数组长度设置为刚好能容纳初始值的长度。
如:int arr[ ] = {1,2,3,4,5};等价于int arr[5] = {1,2,3,4,5};
(3)数组的初始化也可以只针对部分元素,如:int arr[5] = {1,2,3};那么数组的前三个元素分别为1,2,3,剩余的元素被初始化为 0.
(4)如果初始化列表的项数 > 数组元素个数,编译器会将其视为错误。如:int arr[5] = {1,2,3,4,5,6);//error
(5)指定初始化器:初始化指定元素

#include <stdio.h>
int main()
{
    int days[8] = { 31, 28, [4] = 31, 30, 31, [1] = 29};
    int i;
 
    for(i = 0; i < 8; i++)
    {
        printf("%d %d\n",i + 1, days[i]);
    }
 
     return 0;
}

编译并运行该代码,输出如下:

1 31
2 29
3 0
4 0
5 31
6 30
7 31
8 0

指定初始化器的三个重要特性:
①如果指定初始化器后面有更多的值,那么这些值将被初始化指定元素后面的元素,即days [4]被初始化为31后,day[5]和day[6]将分别被初始化为30,31.

②如果再次初始化指定元素,那么最后的初始化将会取代之前的初始化,即day[1]本来被初始化为28,但是day[1]又被后面指定初始化为29.

③如果没有指定元素大小,编译器会把数组的大小设置会足够装得下初始化的值。如:int arr[ ] = {1,[6] = 23}; arr数组有7个元素。

三、一维数组的使用:

  1. [] 为下标引用操作符。数组元素的引用要指定下标,其形式为:数组名 [下标]。
    注意:数组下标从0开始,其取值范围为[0,数组长度-1],下标不能越界。用于识别数组元素的数字被称为下标、索引或者偏移量。所以a[0]的意思就是数组a中的第1个元素(生活中都是从1开始计数的,但是其实在计算机中应该中描述其为第0个元素)。
    2.看以下代码
#include <stdio.h>
int main()
{
    int arr[10] = {0};                  //数组的不完全初始化
    int i;                              //作为数组的下标
    int sz = sizeof(arr)/sizeof(arr[0]);//计算数组的元素个数
 
    //对数组内容进行赋值
    for(i=0; i<10; i++)
    {
        arr[i] = i;
    } 
 
   //输出数组的内容
   for(i=0; i<10; ++i)
   {
       printf("%d ", arr[i]);
   }
 
    return 0;
}
编译并运行该代码,输出如下:

0 1 2 3 4 5 6 7 8 9

(1)数组的大小是可以通过计算得到的。数组的大小是可以通过计算得到的。sizeof(arr)是整个数组的大小,sizeof(arr[0])是数组中一个元素的大小,两者相除得到的就是数组元素的个数。
(2)这段代码使用了一个循环给数组赋值,注意for循环中的第二条表达式(test)中不能写成i <= 10,这样会造成数组下标越界。C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员写代码时,最好自己做越界的检查。
(3)另外,C不允许把数组作为一个单元赋给另一个数组,如:

#include <stdio.h>
int main()
{
    int oxen[5] = {1, 2, 3, 4, 5};
    int yaks[5];
  
    yaks = oxen;//error
 
    return 0;
}

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

看以下代码:

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

编译并运行该代码,输出如下:
在这里插入图片描述

随着数组下标的增长,元素的地址,也在有规律的递增。因为arr数组的类型为int,所以每个地址之间相差4个字节。由此可以得出结论:数组在内存中是连续存放的

五、二维数组的定义和初始化

1.C语言支持多维数组,最常见的多维数组为二维数组。二维数组本质上是以数组作为数组元素的数组,即“数组的数组”。二维数组的定义形式为:类型名 数组名[行长度][列长度];如:int arr[3] [2];//定义了一个二维数组,3行2列,共6个元素。
2.二维数组的初始化:
(1)分行赋初值:一般形式为:类型名 数组名[行长度][列长度]={{初值表0},{初值表1},…,{初值表n}};

如:int arr[2][2] = { { 1, 2} , {3, 4} };
此时arr数组中各元素为:1 2

                                         3 4

二维数组的初始化,也可以只针对部分元素,如:int arr[2][2] = { {1, 2},{} };此时arr数组的第0行的元素的值分别为1,2;而第一行的元素的初值默认为0。

(2)顺序赋初值:一般形式为:类型名 数组名[行长度][列长度]={初值表};
如:int arr[2][2] = { 1, 2, 3, 4 };等价于int arr[2][2] = { { 1, 2} , {3, 4} };

      int arr[2][2] = { 1, 2, 0, 0 };等价于int arr[2][2] = { {1, 2},{} };

(3)注意:二维数组初始化时,如果对全部元素都赋了初值,或分行赋初值时,在初值表中列出了全部行,就可以省略行长度。

如:int arr[ ][3] = {1,2,3,4,5,6,7,8,9};等价于int arr[3][3] = {{1,2,3},{4,5,6},{7,8,9}};

不可以省略列的长度!!!

六、二维数组的使用

1.引用二维数组的元素要指定两个下标,即行下标和列下标,形式为:数组名[行下标][列下标]。行下标的合理取值范围为[0,行长度-1],列下标的合理取值范围为[0,列长度-1]。比如a[0][0]的意思是第1行第1列的元素(生活中从1开始计数),但是其实在计算机中应该中描述其为第0行第0列的元素。
用两重循环来给二维数组赋值:

#include <stdio.h>
int main()
{
    int arr[2][2] = {0};
    int i, j;
 
    for(i = 0; i < 2; i++)
    {
       for(j = 0; j < 2; j++)
       {
          arr[i][j] = i * 4 + j;
       }
    }
 
    for(i = 0; i < 2; i++)
    {
       for(j = 0; j < 2; j++)
       {
           printf("%d ", arr[i][j]);
       }
    }
 
    return 0; 
}
编译并运行代码,输出如下:

0 1 4 5

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

看以下代码:

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

编译并运行该代码,输出如下:
在这里插入图片描述
由此,我们可以分析,二维数组在内存中也是连续存储的。所以,二维数组可以整体看成一个一维数组,如图:

在这里插入图片描述

八、数组作为函数参数

1.数组名是数组首元素的地址,数组名是一个地址常量。

2.两个例外:

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

(2)&数组名,取出的是数组的地址。&数组名,数组名表示整个数组

除了这 两种情况之外,其它所有的数组名都表示数组首元素的地址,如:*数组名等等
看以下代码以及结果:
在这里插入图片描述
①test()中打印的是长度为4,更加印证了数组作为函数参数时,没有把整个数组都传过去,而是把数组中首元素的地址传过去了,此处的地址是用十六进制表示的整型常量,所以大小为4byte。

②main()中打印的长度为40,是因为sizeof(数组名)计算的是整个数组的大小,数组中有10个int型的元素,所以10*4=40

③函数定义头中的形参int arr[]里面的[],它的里面可以什么都不填,也可以填一个任意数,因为它没有意义,比如说形参写成int arr[20]也是可以的,但是为了代码的可读性更强,我们不建议这样写。
其实,test()的形参还可以这样写:

void test(int* arr)
{
    printf("%d\n",sizeof(arr));
}

因为数组名是数组首元素的地址,我们用指针来接收arr的首元素地址更好。注意:只有在函数原型或函数定义头中,才可以用int arr[]代替int* arr。

3.在函数原型中可以省略参数名,所以下面这4种写法等效:

void test(int* arr);
void test(int arr[]);
void test(int*);
void test(int []);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值