在上个系列【C 语言入门】里面,已经对数组和指针定义和使用做了初步的了解。在本文章中,将深入探索:如何使用数组来处理相同类型的批量数据。
数组概述
定义:数组是具有相同数据类型,并且按照一定顺序排列的一组变量的集合。
特征:
- 有序性:数组元素之间具有固定的先后顺序
- 可索引:通过数组名和下标可以唯一地确定数组中的元素
一维数组
一维数组的定义
类型名 数组名[常量表达式]
数组的命名
数组的命名遵循《C 语言标识符的命名规则》 :
- 标识符由字母、数字和下划线组成
- 第一个字母不能是数字,只能是字母或下划线
- 区分大小写字母
- C 语言规定了一个标识符的字符个数。C 语言规定标识符的最大长度可达 255 个字符,只有前 32 个字符在编译时有效。
数组的长度
在定义数组时,需要指定数组元素的个数,即数组长度。
方括号 [ ] 中的常量表达式用于表示数组元素的个数。
Example 1:使用常量表达式指定数组长度
// 定义了一个 int 型的数组,数组长度为 4,具有 4 个数组元素
int a[4];
// 数组下标是从 0 开始
// 注意: 依据上面的定义,数组 a 仅有 4 个数组元素,
// 分别为:a[0]、 a[1]、 a[2]、 a[3],并不存在数组元素 a[4]
a[0] = 0; /* 将数组 a 的 第 0 个 数组元素赋值为 0 */
a[1] = 1; /* 将数组 a 的 第 1 个 数组元素赋值为 1 */
a[2] = 2; /* 将数组 a 的 第 2 个 数组元素赋值为 2 */
a[3] = 3; /* 将数组 a 的 第 3 个 数组元素赋值为 3 */
a[4] = 4; /* 错误! */
Example 2 :使用初始化列表指定数组长度。
// 通过对全部数组元素赋初值来指定数组长度,此时,可以不指定数组长度。
char g_A_Array[] = {0, 1, 2, 3, 4, 5};
printf("g_A_Array's length is %d\r\n", sizeof(g_A_Array));
运行结果:
数组长度不允许动态定义
C 语言不允许对数组的长度做动态定义。即:常量表达式可以包括常量和符号常量,但不能包含变量。
- 常量表达式可以包含常量
int a[4]; // 正确
int b[1 + 3]; // 正确
- 常量表达式可以包含符号常量
#define ArraySize 4
int c[ArraySize]; // 正确
- 常量表达式不可以包含变量
int n;
scanf("%d", &n);
int a[n]; // 错误
NOTE:
如果在被调用的函数(不包含主函数)中定义数组,数组的长度可以是变量或非常量表达式,此数组称为“可变长数组,variable-length arrays(VLAs)”。
Example:VLAs
/* 数组 a 的数组长度表达式为在调用 func 函数时形参 n 从实参得到的值
* 允许在每次调用 func 函数时,n 有不同的值
* 称数组 a 为 可变长数组,variable-length arrays (VLAs)
* VLAs 为 C99 标准中新增的一项
*
* 值得注意的是,Visual Studio 不支持 VLAs
* 若在 Visual Studio 中使用 VLAs,会报 “error C2123:表达式的计算结果不是常量” 的错误
*/
void func(int n)
{
int a[n]; /*正确*/
}
/* 若 a 数组为静态存储方式(static), 则不能用 VLAs,即 funb 的写法是错误的 */
void funb(int n)
{
static int a[n]; /*错误*/
}
一维数组元素的引用
NOTE:只能引用数组元素,而不能一次整体调用整个数组的全部元素的值。
引用数组元素的形式:
数组名[下标]
Example 1 :a[0] 表示数组 a 中序号为 0 的元素。
Example 2:依次给数组 a 的元素赋值,再倒序输出数组 a 的元素。
int main()
{
int a[10]; // 创建数组
int i;
for (i = 0; i < 10; i++)
{
a[i] = i; // 数组元素赋值
}
for (i = 9; i >= 0; i--)
{
printf("a[%d] = %d\r\n", i, a[i]);
}
return 0;
}
输出结果:
NOTE:数组元素的下标由 0 开始。即最后一个数组元素的下标为数组长度减 1。
一维数组的初始化
一维数组的初始化:在定义数组的同时,给各数组元素赋值,这称为数组的初始化。
- 在定义数组时对全部数组元素进行赋初值
Example:
// 花括号 {} 内的数据称为 “初始化列表”
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- 只给一部分数组元素赋初值
Example:仅给 a 数组的前面 5 个元素赋初值
int main()
{
int a[10] = { 0, 1, 2, 3, 4 };
int i;
for (i = 0; i < 10; i++)
{
printf("a[%d] = %d\r\n", i, a[i]);
}
return 0;
}
运行结果:
- 对一维数组中的全部元素值赋值为 0
方式 1:
a[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
方式 2:
a[10] = {0};
- 通过对全部数组元素赋初值来确定数组的元素个数,从而不指定数组长度
Example:
int main()
{
int a[] = { 0, 1, 2, 3, 4 }; // 数组定义并初始化
int i, aLength;
aLength = sizeof(a) / sizeof(int); // 获取数组长度
printf("a's Length = %d\r\n", aLength);
for (i = 0; i < aLength; i++)
{
printf("a[%d] = %d\r\n", i, a[i]);
}
return 0;
}
输出结果:
一维数组的经典应用:冒泡排序
#include <stdlib.h>
int main()
{
int a[10];
int i,j, temp;
printf("Original a:\r\n");
for (i = 0; i < 10; i++)
{
a[i] = rand(); // 生成随机数
printf("a[%d] = %d\r\n", i, a[i]);
}
// 排序过程
for (i = 0; i < 10 - 1; i++)
{
for (j = 0; j < 10 - 1 - i; j++)
{
if (a[j + 1] < a[j])
{
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
printf("After rank:\r\n");
for (i = 0; i < 10; i++)
{
printf("a[%d] = %d\r\n", i, a[i]);
}
return 0;
}
输出结果:
二维数组
二维数组常称为矩阵(matrix)。
NOTE:
- 行(column)
- 列(row)
二维数组的定义
类型名 数组名[常量表达式 col][常量表达式 row]
Example:
a[3][4]; // 定义 3 行 4 列的数组
b[4][3]; // 定义 4 行 3 列的数组
NOTE:
C 语言中,二维数组中元素排列的顺序是按行排列,即在内存中,先顺序存放第 1 行的元素,再存放第 2 行的元素。同时,各个元素都是连续存放的。
Example:使用指针查看二维数组 a 的存放情况。
int main()
{
int a[3][4];
int i,j;
int *addrOfa = &a[0][0];
printf("a:\r\n");
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
a[i][j] = 4*i + j;
printf("a[%d][%d] = %d ,", i, j, a[i][j]);
}
printf("\r\n");
}
printf("a's Base Address: %x\r\n", addrOfa);
for (i = 0; i < (3 * 4); i++)
{
printf("Addr %x = %d\r\n", (addrOfa + i), *(addrOfa+i));
}
getchar();
return 0;
}
运行结果:
通过运行结果,可得知:
- 在内存中,先顺序存放第 1 行的元素,再存放第 2 行的元素。
- 各个元素都是连续存放的。
二维数组元素的引用
二维数组元素的引用形式:
数组名[下标 col][下标 row]
Example:
a[0][1] 表示数组 a 中第 0 行第 1 列的元素。
a[8][9] 表示数组 a 中第 8 行第 9 列的元素。
NOTE:
二维数组的下标均从 0 开始。
二维数组的初始化
- 分行给二维数组初始化
- 将所有数据写在一个花括号内,按数组元素在内存中的排序顺序对各个元素赋初值
- 对部分元素进行赋初值
Example:
int a[3][4] = { {0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11} };
二维数组的实例
Example:行列置换。
源数组:
aArray = {{1, 2, 3},
{4, 5, 6}}
目标数组:
bArray = {{1, 4},
{2, 5},
{3, 6}}
实现代码:
#include "stdio.h"
#define aLine 2
#define aColumn 3
int a[aLine][aColumn] = { { 1, 2, 3 },
{ 4, 5, 6 } };
int b[aColumn][aLine];
static void OutputA(void)
{
int i, j;
printf("a[%d][%d]={\r\n", aLine, aColumn);
for (i = 0; i < aLine; i++)
{
printf("{");
for (j = 0; j < aColumn; j++)
{
printf("%d", a[i][j]);
if (j != (aColumn - 1)){
printf(", ");
}
}
printf("}");
if (i != (aLine - 1)){
printf(", ");
}
printf("\r\n");
}
printf("}\r\n");
}
static void OutputB(void)
{
int i, j;
printf("b[%d][%d]={\r\n",aColumn , aLine);
for (i = 0; i < aColumn; i++)
{
printf("{");
for (j = 0; j <aLine; j++)
{
printf("%d", b[i][j]);
if (j != (aLine - 1)) {
printf(", ");
}
}
printf("}");
if (i != (aColumn - 1)) {
printf(", ");
}
printf("\r\n");
}
printf("}\r\n");
}
int main(void)
{
int i, j;
OutputA();
for (i = 0; i < aLine; i++)
{
for (j = 0; j < aColumn; j++)
{
b[j][i] = a[i][j];
}
}
OutputB();
}
输出结果:
字符数组
字符型数据是以字符的 ASCII 码形式存放在内存单元中的。C 语言中没有字符串类型,字符串是存放在字符型数组中的。
字符数组的定义
字符数组:用来存放字符数据的数组,字符数组中的一个元素存放一个字符。
char 数组名[数组长度]
字符数组的初始化
使用初始化列表对字符数组进行初始化,把各个字符依次赋给数组中的各个元素。
Example:把 10 个字符依次赋给 a[0] ~ a[10]。
char a[10] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y'};
NOTE:
如果在定义字符数组时不进行初始化,则数组中的各个元素的值是不可预料的。
- 若花括号
{}
中提供的初值个数大于数组长度,则出现语法错误 - 如初值个数小于数组长度,则只将这些字符赋给数组中前面的元素,其余的元素自动定为空字符,即
'\0'
Example:打印字符数组 a 元素的字符值和整型值。
int main(void)
{
char a[10] = { 'I', ' ', 'a', 'm' };
int i;
for (i = 0; i < 10; i++)
{
printf("a[%d] = '%c'(char), %3d(int)\r\n", i, a[i], a[i]);
}
return 0;
}
输出结果:
- 若提供的初值个数与预期的数组长度相同,则可省略数组长度,系统会自动根据初值个数确定数组长度。
Example:
char a[ ] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y'};
字符数组的元素引用
可以通过引用字符数组的一个元素,得到一个字符。
Example:打印已知的字符串。
Example:打印字符数组 a 元素的字符值和整型值。
int main(void)
{
char a[10] = { 'I', ' ', 'a', 'm' };
int i;
printf("a[10] = \"%s\"\r\n", a);
for (i = 0; i < 10; i++)
{
printf("a[%d] = '%c'(char), %3d(int)\r\n", i, a[i], a[i]);
}
getchar();
}
输出结果:
在输出结果中,能发现 使用 %s
进行打印时,a 仅输出了 4个字符:‘I’, ‘ ’, ‘a’, ‘m’。这是因为 ‘\0’ (整型值为 0)是 C 语言规定的 “字符串结束标记”,也就是说在遇到字符 ‘\0’ 时,表示字符串结束,把它之前的字符组合成一个字符串。C 系统中,在用字符数组存储字符串常量时会自动加一个 ‘\0’ 作为结束字符。
在执行 printf 函数时,没输出一个字符检测一次,看下一个字符是否为 ‘\0’, 遇到 ‘\0’ 就停止输出。因此,上例子,仅输出了 I am
。