浅谈数组(C 语言)

目录

  • 一、何为数组?
  • 二、一维数组
    • [1. 一维数组的创建和初始化](#1. 一维数组的创建和初始化)
      • [1.1 字符数组的初始化](#1.1 字符数组的初始化)
    • [2. 一维数组的使用](#2. 一维数组的使用)
      • [2.1 使用下标打印数组元素](#2.1 使用下标打印数组元素)
      • [2.2 使用下标输入数组元素](#2.2 使用下标输入数组元素)
    • [3. 一维数组在内存中的存储](#3. 一维数组在内存中的存储)
    • [4. 使用 sizeof 计算数组元素个数](#4. 使用 sizeof 计算数组元素个数)
  • 三、二维数组
    • [1. 二维数组的创建](#1. 二维数组的创建)
    • [2. 二维数组的初始化](#2. 二维数组的初始化)
    • [3. 二维数组的使用](#3. 二维数组的使用)
    • [4. 二维数组在内存中的存储](#4. 二维数组在内存中的存储)
  • 四、数组名的含义
    • [1. 从函数传参解释数组名的含义](#1. 从函数传参解释数组名的含义)

一、何为数组?

在 C 语言中,数组是一组具有相同数据类型的元素的集合。数组是在已有的类型的基础上创建的复合类型,已有的类型可以是 C 语言内置的,也可以是自定义的类型。

二、一维数组

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

(1)数组的创建

创建数组需要数组的大小和数组的类型,一般格式如下:类型 数组名[大小];

如下代码:

c 复制代码
// 数组的创建和初始化的演示
int arr1[10];
double arr2[20];
char arr3[30];

上述代码分别创建了,大小为 10 的 int 数组 arr1,大小为 20 的 double 数组 arr2,大小为 30 的char 数组 arr3。

注意: 创建数组时,其大小只能使用常量表达式来指定。如果你的编译器支持 C99,那么可以使用变量来指定(变长数组)。

(2)数组初始化

数组的初始化使用花括号来完成,其中的元素使用逗号隔开。数组初始化又分为完全初始化和不完全初始化,完全初始化就是初始化的元素个数和数组的大小一致,不完全初始化就是初始化的元素个数少于数组的元素个数。初始化都是从前往后一一对应,不完全初始化剩余的元素均被设置为 0。如下代码:

数组初始化时可以不指定数组的大小,编译器会把该数组初始化元素的个数指定为其大小。如下代码:

使用 sizeof(数组名) / sizeof(首元素) 可以获得该数组的大小,当然只能在创建该数组的地方使用。

1.1 字符数组的初始化

由于字符数组可以存储字符串,所以字符数组的初始化方式有两种。如下代码:

c 复制代码
char arr1[3] = { 'a', 'b', 'c' };  // 字符数组
char arr2[4] = "abc";  // 字符串

上述两个数组都是字符数组,但是只有第二个数组是字符串。因为字符串是以空字符结尾的,第一个数组末尾没有空字符。而第二个数组编译器会在末尾添加空字符。

从上述图片中,可以看到数组 arr2 的末尾有空字符。也可以通过打印字符串来进行验证,使用 printf() 函数打印字符串时,遇到空字符结束。如下代码:

2. 一维数组的使用

一维数组通过下标运算符来访问数组元素,且下标是从 0 开始的。如:

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

arr[0] 就是该数组的第一个元素,arr[4] 就是该数组的最后一个元素。

2.1 使用下标打印数组元素

只要知道数组的大小,通过循环和一个整型变量从 0 递增到 size - 1,就可以访问这个数组的所有元素。如下代码:

2.2 使用下标输入数组元素

同理和上述代码类似,我们也可以对数组的元素进行逐个输入。如下代码:

3. 一维数组在内存中的存储

一维数组在内存中的存储是连续的,拿 int 数组来说,相邻元素之间相差 sizeof(int) 个字节。如:int arr[5],其中 arr[0] 和 arr[1] 之间相差四个字节。可以通过指针变量来打印地址进行验证,如下代码:

4. 使用 sizeof 计算数组元素个数

可以在创建该数组的代码块中,通过 sizeof(数组名) / sizeof(数组首元素) 来计算该数组的元素个数。前者是数组总大小,后者是数组每个元素的大小。如下代码:

三、二维数组

二维数组,实际上就是元素是数组的数组。我们可以理解为一表格,有行和列。但实际存储时,仍是连续的,先存储第一行然后存储第二行,以此类推。

1. 二维数组的创建

以 int 类型的二维数组为例,如下代码:

c 复制代码
// 二维数组
int arr[3][4];

创建了一个包含 3 个元素的数组,每个元素是一个包含 4 个元素的 int 数组。把数组名和后面的方括号去掉,剩下的 int [4] 就是该数组元素的类型。其他数组以此类推。

2. 二维数组的初始化

二维数组可以通过两个下标相乘计算其元素总个数,所以可以像一位数组那样初始化,如下代码:

c 复制代码
// 二维数组初始化
int arr[3][4] = { 1,2,3,4,5,6,7,8,9 };

和一维数组一样,这是不完全初始化,未初始化的元素值被设置为 0。可以想象成:

1 2 3 4

5 6 7 8

9 0 0 0

由于二维数组的元素为数组,也可以把数组当作一个元素(用花括号括起)初始化,如下代码:

c 复制代码
int arr[3][4] = { {1,2,3}, {4,5,6}, {7,8,9} };

上述每个数组都采用部分初始化,每个数组的最后一个元素均为 0。

可以想象成:

1 2 3 0

4 5 6 0

7 8 9 0

一维数组初始化时可以省略数组大小,编译器根据数组初始化元素个数进行计算。而二维数组可以省略行数,但不能省略列数,编译器根据初始化元素总个数除以列数来计算行数。如下代码:

3. 二维数组的使用

一维数组可以通过下标进行访问,二维数组同样可以。二维数组使用两个下标进行定位,第一个下标代表行,第二个下标代表列,同样都是从 0 开始,只要是数组的下标都是从 0 开始。

(1)使用下标对二维数组进行输入

c 复制代码
// 使用下标对二维数组进行输入
int arr[3][4];
int i;
for (i = 0; i < 3; ++i)  // 控制行
{
	int j;
	for (j = 0; j < 4; ++j)  // 控制每行的列
		scanf("%d", &arr[i][j]);
}

(2)使用下标打印二维数组

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

由一位数组在内存中是连续存储的,可以猜测二维数组在内存中也是连续存储的。因为二维数组本身也可以看成一维数组,只不过其元素也是数组。通过使用指针变量打印每个元素的地址进行验证。代码如下:

可以看到二维数组中的每个元素在内存中都是连续的存储的。如果把二维数组看成一维数组,那么每个数组之间相邻 16 字节。

四、数组名的含义

这里先给出结论,数组名实际上是数组首元素的地址。但是除了以下两种情况:

(1)sizeof(数组名)这里求得的是整个数组的大小

(2)&数组名 这里求得的是整个数组的地址

给出代码验证:

可以看到,数组名实际上就是数组首元素的地址。接下来给这三个变量加 1,如下:

可以看到 &arr + 1 增加了十六进制的 14 也就是 20,也就是整个数组的大小。而指针变量加 1,增加的是其指向对象的大小。所以,&数组名 取出的是整个数组的地址。

1. 从函数传参解释数组名的含义

我们都知道 C 语言函数传参是值传递,也就是被调函数中的函数参数实际上是主调函数中变量的副本。假设我们传参的时候传递了整个数组,那么当数组很大时所进行的拷贝就大大提高了时间和空间的消耗。而如果函数名是数组首元素的地址的话,又因为数组在内存中是连续存储的,我们只要传递该数组的元素个数就可以访问整个数组。这样极大地提高了时间和空间的使用率。

而实际上声明函数时,我们使用数组来接收实际上就是使用指针来接受,因为指针变量是存储地址的。也就是下面两个函数参数是等价的:

c 复制代码
int Sum1(int arr[]);
int Sum2(int *pi);

使用数组来接收显得更加通俗易懂,但是其本质是一个指针。而我们使用的下标运算符实际上等价于指针移动后的解引用操作,如:arr[3] 等价于 *(arr+3) 。这个下标运算符可以称为语法糖,让我们更加容易使用数组。

相关推荐
一路冰雨几秒前
Qt打开文件对话框选择文件之后弹出两次
开发语言·qt
St_Ludwig3 分钟前
C语言 蓝桥杯某例题解决方案(查找完数)
c语言·c++·后端·算法·游戏·蓝桥杯
是糖不是唐5 分钟前
代码随想录算法训练营第五十三天|Day53 图论
c语言·数据结构·算法·图论
松树戈9 分钟前
JS推荐实践
开发语言·javascript·ecmascript
瑞雨溪12 分钟前
java中的this关键字
java·开发语言
努力的小雨27 分钟前
零基础入门gRPC:从 0 实现一个Hello World
经验分享
MapleLea1f36 分钟前
26届JAVA 学习日记——Day14
java·开发语言·学习·tcp/ip·程序人生·学习方法
小汤猿人类36 分钟前
SpringTask
开发语言·python
爪哇学长43 分钟前
解锁API的无限潜力:RESTful、SOAP、GraphQL和Webhooks的应用前景
java·开发语言·后端·restful·graphql
老赵的博客1 小时前
QT 自定义界面布局要诀
开发语言·qt