1.一维数组
1.1 数组概念
- 数组是含有多个数据值的数据结构,并且每个数据值具有相同的数 据类型。这些数据值称为元素 (element),可以根据元素在数组中 所处的位置把它们一个个地选出来,数组元素是存储到一片连续的内存中的。
- 最简单的数组类型就是一维数组,一维数组中的元素一个接一个地编 排在单独一行,这里可以假设 有一个名为a 的一维数组:
- 为了声明数组,需要指明数组元素的类型和数量 。例如,为了声明 数组a 有10个int 类型的元素,可以写成
int a[10];
/*
此时数组 a 中有 10 个元素,每个元素都是 int 型变量,
int 型变量占 4 字节的内存空间,那么 10 个 int 型变量就占 40 字节的内存空间,
而且它们的地址是连续分配的。
*/
- a 是数组名,即这片连续内存的名称
- [10] 代表这片连续内存总共分成10个相等的格子,每个格子称为数组的元素
- int 代表每个元素的类型,可以是任意基本类型,也可以是组合类型,甚至可以是数组
1.2 数组初始化
- 在定义的时候赋值,称为初始化
int a[5] = {1, 2, 3, 4, 5};
- 通过将数组元素的初值依次放在一对大括号中,如此初始化之后,a[0]=1;a[1]=2;a[2]=3;a[3]=4;a[4]=5,即从左到右依次赋给每个元素。
- 需要注意的是,初始化时各元素间是用逗号隔开的,不是用分号。
- 可以只给一部分元素赋值,这叫“不完全初始化”
int a[5] = {1, 2};
- 定义的数组 a 有 5 个元素,但大括号内只提供两个初值,这表示只给前面两个元素 a[0]、a[1] 初始化,而后面三个元素都没有被初始化。不完全初始化时,没有被初始化的元素自动为 0。
- 需要注意的是,“不完全初始化”和“完全不初始化”不一样。如果“完全不初始化”,即只定义“int a[5];”而不初始化,那么各个元素的值就不是0了,所有元素都是垃圾值。
- 如果定义数组时就给数组中所有元素赋初值,那么就可以不指定数组的长度,因为此时元素的个数已经确定了,系统会自动分配空间
int a[] = {1, 2, 3, 4, 5};
- 元素下标:数组开头位置的偏移量,下标从 0 开始,即从 a[0] 开始,而不是 a[1]
#include <stdio.h>
int main(void)
{
int a[5] = {1, 2, 3, 4, 5};
int i;
for (i = 0; i < 5; i++)
{
printf("%d\n", a[i]);
}
return 0;
}
1.3 数组元素引用
- 数组必须先定义,然后使用。C 语言规定,只能逐个引用数组元素,而不能一次引用整个数组,数组下标可以是整型常量或整型表达式
a[0] = a[5] + a[7] - a[2 * 3]
- 注意:定义数组时用到的“数组名[常量表达式]”和引用数组元素时用到的“数组名[下标]”是有区别的,定义数组时的常量表达式表示的是数组的长度,而引用数组元素时的下标表示的是元素的编号
#include <stdio.h>
int main(void)
{
int a[5] = {1, 2, 3, 4, 5}; // 定义长度为5的数组a
int t;
t = a[3]; // 引用数组a中下标为3的元素a[3], 此时的3不代表数组的长度
printf("t = %d\n", t);
return 0;
}
// t = 4
2.二维数组
- 若数组元素类型也是数组,则该数组称为二维数组
int a[2][3];
// a[2] 是数组的定义,表示该数组拥有两个元素
// int [3]是元素的类型,表示该数组元素是一个具有三个元素的整型数组
- 多维数组的语法跟普通的一维数组语法完全一致
- 初始化
int a[2][3] = {{1,2,3}, {4,5,6}}; // 数组的元素是另一个数组
int a[2][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; // 错误,越界了
int a[2][3] = {{1,2,3}, {4,5,6,7}}; // 错误,越界了
int a[ ][3] = {{1,2,3}, {4,5,6}}; // OK,自动根据初始化列表分配数组元素个数
int a[2][3] = {{1,2,3}}; // OK,只初始化数组元素的一部分
- 元素引用
int a[2][3] = {{1,2,3}, {4,5,6}}; // 数组的元素是另一个数组
// a[0] 代表第一个元素,这个元素是一个具有 3 个元素的数组:{1,2,3}
// a[1] 代表第二个元素,这个元素也是一个具有 3 个元素的数组:{4,5,6}
printf("%d", a[0][0]); // 输出第一个数组的第一个元素,即1
printf("%d", a[1][2]); // 输出第二个数组的第三个元素,即6
- 任意的数组,不管有多复杂,其定义都由两部分组成。
- 第1部分:说明元素的类型
- 第2部分:说明数组名和元素个数
int a[4]; // 第2部分:a[4]; 第1部分:int
int b[3][4]; // 第2部分:b[3]; 第1部分:int [4]
int c[2][3][4]; // 第2部分:c[2]; 第1部分:int [3][4]
int *d[6]; // 第2部分:d[6]; 第1部分:int *
int (*e[7])(int, float); // 第2部分:e[7]; 第1部分:int (*)(int, float)
- 注意:
- 上述示例中,a[4]、b[3]、c[2]、d[6]、e[7]本质上并无区别,它们均是数组
- 上述示例中,a[4]、b[3]、c[2]、d[6]、e[7]唯一的不同,是它们所存放的元素的不同
- 第1部分的声明语句,如果由多个单词组成,C语言规定需要将其拆散写到第2部分的两边
3.字符数组
- 用来存放字符的数组称为字符数组
char a[10]; // 一维字符数组
char b[5][10]; // 二维字符数组
char c[20] = {'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm'}; // 给部分数组元素赋值
char d[] = {'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm'}; // 对全体元素赋值时可以省去长度
- 字符数组实际上是一系列字符的集合,也就是字符串(String)。
- 在C语言中,没有专门的字符串变量,没有string类型,通常就用一个字符数组来存放一个字符串。
- 字符串是一系列连续的字符的组合,要想在内存中定位一个字符串,除了要知道它的开头,还要知道它的结尾。找到字符串的开头很容易,知道它的名字(字符数组名或者字符串名)就可以;然而,如何找到字符串的结尾呢?
- 在C语言中,字符串总是以'\0'作为结尾,所以'\0'也被称为字符串结束标志,或者字符串结束符。
- '\0'是 ASCII 码表中的第 0 个字符,英文称为 NULL,中文称为“空字符”。该字符既不能显示,也没有控制功能,输出该字符不会有任何效果,它在C语言中唯一的作用就是作为字符串结束标志。
- C语言在处理字符串时,会从前往后逐个扫描字符,一旦遇到'\0'就认为到达了字符串的末尾,就结束处理。'\0'至关重要,没有'\0'就意味着永远也到达不了字符串的结尾。
- 由" "包围的字符串会自动在末尾添加'\0'。例如,"abc123"从表面看起来只包含了 6 个字符,其实不然,C语言会在最后隐式地添加一个'\0',这个过程是在后台默默地进行的,所以我们感受不到。
- 在C语言中,字符串总是以'\0'作为结尾,所以'\0'也被称为字符串结束标志,或者字符串结束符。
- 当用字符数组存储字符串时,要特别注意'\0',要为'\0'留个位置;这意味着,字符数组的长度至少要比字符串的长度大1
char str[7] = "abc123";
/*
"abc123"看起来只包含了 6 个字符,我们却将 str 的长度定义为 7,就是为了能够容纳最后的'\0',
如果将 str 的长度定义为 6,它就无法容纳'\0'了。
*/
- C语言规定,可以将字符串直接赋值给字符数组
char s1[5] = {'a', 'b', 'c', 'd', 'e'}; // s1存放的是字符序列,非字符串
char s2[6] = {'a', 'b', 'c', 'd', 'e', '\0'}; // s2存放了一个字符串
char s[6] = {"abcde"}; // 使用字符串直接初始化字符数组
char s[6] = "abcde"; // 大括号可以省略,这种形式更加简洁,实际开发中常用
s[0] = 'A'; // 索引第一个元素,赋值为 'A'
// 为了方便,你也可以不指定数组长度
char str[] = "www.baidu.com";
- 所谓字符串长度,就是字符串包含了多少个字符(不包括最后的结束符'\0')。例如"abc"的长度是 3,而不是 4。
4.变长数组
- 数组变量的长度必须用常量表达式进行定义。但是在 C99中,有时候也可以使用非常量表达式。
- 定义时,使用变量作为元素个数的数组。
- 变长数组仅仅指元素个数在定义时是变量,而绝非指数组的长度可长可短。实际上,不管是普通数组还是所谓的变长数组,数组一旦定义完毕,其长度则不可改变。
#include <stdio.h>
int main(void)
{
int i, n;
printf("请输入数组长度\n");
scanf("%d", &n);
int a[n]; // C99 only - length of array depends on n
printf("Enter %d numbers: ", n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
printf("数组元素如下\n:");
for (i = n - 1; i >= 0; i--)
printf(" %d", a[i]);
printf("\n");
return 0;
}
- 上面程序中的数组 a 是一个变长数组,变长数组的长度是在程序执行时计算的,而不是在程序编译 时计算的。变长数组的主要优点是程序员不必在构造数组时随便给定 一个长度,程序在执行时可以准确地计算出所需的元素个数。如果让 程序员来指定长度,数组可能过长(浪费内存)或过短(导致程序出 错)。
- 数组 a 的长度由用户的输入确定而 不是由程序员指定一个固定的值,这是与老版本不同的地方。
注意:变长数组不可初始化,即以下代码是错误的:
int len = 5;
int a[len] = {1,2,3,4,5}; // 数组 a 不可初始化