按序排列的同类型数据元素的集合称为数组。在C语言中,数组属于构造数据类型。数组中的数组元素可以是基本数据类型,也可以是构造类型。因此数组按元素类型的不同可分为:数值数组、字符数组、指针数组、结构数组等。
7.1 一维数组的定义和引用
7.1.1 一维数组的定义方式
在C语言中使用数组必须先定义,基本语法如下:
// 语法
类型说明符 数组名[常量表达式];
// 示例
int a[10];// 整型数组a,有10个元素
float b[20];// 浮点数数组,有10个元素
char c[20];// 字符数组,有20个元素
说明:
- 类型说明符是任一种基本数据类型或构造数据类型。如
int
、float
等。 - 数组名是用户定义的数组标识符。
- 方括号中的常量表达式表示数据元素的个数,也称为数组的长度。
注意事项:
- 数组的类型实际上是指数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相同的。如
int a[20];
中所有元素都是整型。 - 数组名的书写规则应符合标识符的书写规定。
- 数组名不能与其它变量名相同。如
int a;
和float a[10];
不能同名。 - 方括号中常量表达式表示数组元素的个数,如a[5]表示数组a有5个元素。但是其下标从0开始计算。因此5个元素分别为a[0]、a[1]、a[2]、a[3]、a[4]。
- 不能在方括号中用变量来表示元素的个数,但是可以是符号常数或常量表达式。如
int n=5; int a[n];
是错误的;如int a[3+2];
是正确的。 - 允许在同一个类型说明中,说明多个数组和多个变量。如
int a,b,c,m[10],n[20];
。
7.1.2 一维数组元素的引用
数组元素是组成数组的基本单元。数组元素也是一种变量, 其标识方法为数组名后跟一个下标。下标表示了元素在数组中的顺序号,下标从0开始,以数组长度减去1结束。数组元素的语法如下:
数组名[下标]
说明:
- 下标只能为整型常量或整型表达式。如为小数时,C编译将自动取整。如
a[5]
、a[i+j]
、a[i++]
都是合法的数组元素。 - 数组元素通常也称为下标变量。必须先定义数组,才能使用下标变量。
- 在C语言中只能逐个地使用下标变量,而不能一次引用整个数组,如
int a[5]; printf("%d", a)
是错误的。
示例:
#include <stdio.h>
int main() {
// 定义数组
int nums[10];
// 给数组中所有元素赋值
for (int i = 0; i < 10; i++) {
nums[i] = i * 10;// 通过下标访问数组元素并且赋值
}
// 打印赋值后数组的所有元素
for (int i = 0; i < 10; ++i) {
printf("%d\t", nums[i]);
}
}
7.1.3 一维数组的初始化
给数组赋值的方法有:
- 利用赋值语句给数组元素逐个赋值。如
nums[2]=22;
。 - 初始化赋值。即在数组定义的同时给数组赋予初值,在编译阶段进行的,这样将减少运行时间,提高效率。
- 动态赋值。
初始化赋值的语法如下:
// 语法,其中在{ }中的各数据值即为各元素的初值,各值之间用逗号间隔
类型说明符 数组名[常量表达式]={值, 值, ..., 值};
// 示例
int a[10]={0,1,2,3,4,5,6,7,8,9};// 等价于a[0]=0;a[1]=1;...a[9]=9
数组初始化赋值的注意事项:
- 可以只给部分元素赋初值。当
{ }
中值的个数少于元素个数时,只 给前面部分元素赋值。如int a[10]={0,1,2,3,4};
表示只给a[0]~a[4]五个元素赋值,而后五个元素自动赋为零值。 - 只能给元素逐个赋值,不能给数组整体赋值。如给数组是个元素全部赋为1值,只能写成:
int a[10]={1,1,1,1,1,1,1,1,1,1};
。 - 给全部元素赋值,可以不给出数组元素的个数。如
int a[5]={1,2,3,4,5};
可以写成int a[]={1,2,3,4,5};
。
示例:
#include <stdio.h>
int main() {
// 定义数组
int nums[10], max;
// 从键盘输入10个数存入数组
for (int i = 0; i < 10; ++i) {
scanf("%d", &nums[i]);
}
// 然后计算数组中的最大值
max = nums[0];
for (int i = 0; i < 10; ++i) {
if (nums[i] > max) {
max = nums[i];
}
}
// 打印结果
printf("max=%d", max);
}
7.2 二维数组的定义和引用
7.2.1 二维数组的定义
C语言允许构造多维数组。多维数组元素有多个下标,以标识它在数组中的位置,所以也称为多下标变量。二维数组的定义语法如下:
// 语法
类型说明符 数组名[常量表达式1][常量表达式2];
// 示例
int a[3][4];
说明:
- 量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度。如
int a[3][4];
表示一个三行四列的数组,数组名为a。 - 注意,实际的硬件存储器却是连续编址的,也就是说存储器单元是按一维线性排列的。如何在一维存储器中存放二维数组,可有两种方式:一种是按行排列, 即放完一行之后顺次放入第二行。另一种是按列排列, 即放完一列之后再顺次放入第二列。在C语言中,二维数组是按行排列的。
7.2.2 二维数组元素的引用
引用二维数组元素的语法如下:
数组名[下标][下标]
说明:
- 下标应为整型常量或整型表达式。如
a[3][4]
。
示例:
#include <stdio.h>
int main() {
// 设一个二维数组scores[5][3]存放五个人三门课的成绩。再设一个一维数组avg[3]存放所求得各分科平均成绩,设变量average为全组各科总平均成绩。
int scores[5][3], avg[3], sum, average;
for (int i = 0; i < 3; i++) {// 各科
printf("请输入第%d科各人的成绩:\n", (i + 1));
for (int j = 0; j < 5; j++) {// 各人
printf("\t请输入第%d个人的成绩:", (j + 1));
scanf("%d", &scores[j][i]);
sum += scores[j][i];
}
avg[i] = sum / 5;
sum = 0;
}
average = (avg[0] + avg[1] + avg[2]) / 3;// 计算各科总平均成绩
printf("各分科平均成绩:%d, %d, %d\n", avg[0], avg[1], avg[2]);
printf("各科总平均成绩:%d", average);
}
7.2.3 二维数组的初始化
二维数组初始化在数组定义的同时并进行赋值。二维数组可按行分段赋值,也可以按行连续赋值。如对数组a[5][3]
初始化赋值:
- 按行分段赋值:
int a[5][3]={{80,75,92},{61,65,71},{59,63,70},{85,87,90},{76,77,85}};
。 - 按行连续赋值:
int a[5][3]={80,75,92,61,65,71,59,63,70,85,87,90,76,77,85};
。
#include <stdio.h>
int main() {
// 按行分段赋值
int a[5][3] = {{80, 75, 92},
{61, 65, 71},
{59, 63, 70},
{85, 87, 90},
{76, 77, 85}};
// 按行连续赋值
int b[5][3] = {80, 75, 92, 61, 65, 71, 59, 63, 70, 85, 87, 90, 76, 77, 85};
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 3; ++j) {
printf("%d\t", a[i][j]);
}
printf("\n");
}
}
二维数组初始化赋值的注意事项:
- 可以只对部分元素赋初值,未赋初值的元素自动取0值。如
int a[3][3]={{1},{2},{3}};
是对每一行的第一列元素赋值,未赋值的元素取0值。 - 如对全部元素赋初值,则第一维的长度可以不给出。如
int a[3][3]={1,2,3,4,5,6,7,8,9};
可以写为int a[][3]={1,2,3,4,5,6,7,8,9};
,因为第一维的长度可以计算出来。 - 数组是一种构造类型的数据。二维数组可以看作是由一维数组的嵌套而构成的。设一维数组的每个元素都又是一个数组,就组成了二维数组。一个二维数组也可以分解为多个一维数组。如二维数组
a[3][4]
可以分解为三个一维数组,其数组名分别为:a[0]
、a[1]
、a[2]
。并且这三个一维数组都有4个元素,例如:一维数组a[0]
的元素为a[0][0]
、a[0][1]
、a[0][2]
、a[0][3]
。 a[0]
、a[1]
、a[2]
不能当作下标变量使用,它们是数组名,不是一个单纯的下标变量。
#include <stdio.h>
int main() {
// 可以只对部分元素赋初值,未赋初值的元素自动取0值
int a[3][3] = {{1},
{2},
{3}};
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
printf("%d\t", a[i][j]);
}
printf("\n");
}
// 如对全部元素赋初值,则第一维的长度可以不给出
int b[][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
}
/*
1 0 0
2 0 0
3 0 0
*/
7.3 字符数组
用来存放字符量的数组称为字符数组。
7.3.1 字符数组的定义
与前面定义数值数组的语法一样,如:
- 定义一维字符数组:
char c[10];
。 - 定义二维或多维数组:
char c[5][10];
。
7.3.2 字符数组的初始化
字符数组也可以在定义的同时就初始化赋值。如char c[12]={'h','e','l','l','o',' ', 'w', 'o', 'r', 'l', 'd'};
。
#include <stdio.h>
int main() {
// 字符数组也允许在定义时作初始化赋值。
char c[12] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
for (int i = 0; i < 15; ++i) {
printf("%c\t", c[i]);
}
// 也允许对全体元素赋初值时省去长度说明
char d[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
}
7.3.3 字符数组的引用
通过下标引用字符数组中的元素即可。
#include <stdio.h>
int main() {
int i, j;
char a[][5] = {{'B', 'A', 'S', 'I', 'C',},
{'d', 'B', 'A', 'S', 'E'}};
for (i = 0; i <= 1; i++) {
for (j = 0; j <= 4; j++) {
printf("%c", a[i][j]);
}
printf("\n");
}
}
7.3.4 字符串和字符串结束标志
在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。当把一个字符串存入一个数组时,也把结束符'\0'
存入数组,并以此作为该字符串是否结束的标志。有了'\0'
标志后,就不必再用字符数组的长度来判断字符串的长度了。
C语言允许用字符串的方式对数组作初始化赋值。有下面三种方式:
char c[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
char c[]={"hello world"};
char c[]="hello world";
注意,用字符串方式赋值比用字符逐个赋值要多占一个字节,因为需要存放字符串结束标志'\0'
。所以上面的数组c在内存中的实际存放情况是:
h | e | l | l | o | w | o | r | l | d | \0 |
---|
'\0'
是由C编译系统自动加上的。由于采用了'\0'
标志,所以在用字符串赋初值时一般无须指定数组的长度,而由系统自行处理。
7.3.5 字符数组的输入输出
除了上述用字符串赋初值的办法外,还可用printf
函数和scanf
函数一次性输出输入一个字符数组中的字符串,而不必使用循环语句逐个地输入输出每个字符。
- 利用
printf
函数一次性输出字符串。
#include <stdio.h>
int main() {
// 声明一个字符串
char c[] = "hello world";
// 利用%s一次性输出字符串
printf("%s", c);
}
- 利用
scanf
函数一次性输入字符串。
#include <stdio.h>
int main() {
// 创建字符数组,用来存放字符串
char str[15];
// 一次性输入字符串
scanf("%s", str);// 注意不是&str
// 一次性输出字符串
printf("%s", str);
}
当使用scanf
函数一次性输入字符串时需要注意如下几点:
- 对于定义一个字符数组,如果不初始化赋值,则必须声明数组长度。并且输入字符串的长度应该小于定义的数组的长度,以留出一个字节用于存放字符串结束标志
'\0'
。 - 当用
scanf
函数输入字符串时,字符串中不能含有空格,否则将以空格作为串的结束符。如输入hello world
则只会保留hello
。 - 注意
scanf("%s", str);
而不是scanf("%s", &str);
,是直接传入了数组名。因为在C语言中规定,数组名代表了该数组的首地址,整个数组是以首地址开头的一块连续内存单元。因此scanf("%s", &str);
是错误的。 - 如设数组str的首地址为2000,即
str[0]
单元地址为2000,则数组名str就代表这个首地址,因此不能在str前面加地址运算符&
。在执行函数printf("%s", str)
时,按数组名str找到首地址,然后逐个输出数组中各个字符直到遇到字符串终止标志'\0'
为止。
7.3.6 字符串处理函数
C语言提供了丰富的字符串处理函数, 大致可分为字符串的输入、输出、合并、修改、比较、转换、复制、搜索几类。
- 如果想要使用输入输出的字符串函数,则应该引入
"stdio.h"
库文件。 - 如果想要使用其他字符串函数,则应该引入
"string.h"
库文件。
常用的字符串函数:
函数 | 语法 | 功能 | 备注 |
---|---|---|---|
字符串输出函数 | puts(字符数组名) | 把字符数组中的字符串输出到显示器。 即在屏幕上显示该字符串。 | puts 函数完全可以由printf 函数取代。当需要按一定格式输出时,通常使用printf 函数。 |
字符串输入函数 | gets(字符数组名) | 从标准输入设备键盘上输入一个字符串。 | 本函数得到一个函数值,即为该字符数组的首地址。并且gets 函数并不以空格作为字符串输入结束的标志,而只以回车作为输入结束。这是与scanf 函数不同的。 |
字符串连接函数 | strcat(字符数组名1, 字符数组名2) | 把字符数组2中的字符串连接到字符数组1 中字符串的后面,并删去字符串1后的串标志"\0" 。 | 本函数返回值是字符数组1的首地址。要注意的是,字符数组1应定义足够的长度,否则不能全部装入被连接的字符串。 |
字符串拷贝函数 | strcpy(字符数组名1, 字符数组名2) | 把字符数组2中的字符串拷贝到字符数组1中。串结束标志"\0" 也一同拷贝。字符数名2,也可以是一个字符串常量。这时相当于把一个字符串赋予一个字符数组。 | 本函数要求字符数组1应有足够的长度,否则不能全部装入所拷贝的字符串。 |
字符串比较函数 | strcmp(字符数组名1, 字符数组名2) | 按照ASCII码顺序比较两个数组中的字符串,并由函数返回值返回比较结果。如果字符串1等于字符串2则返回值等于0;如果字符串1大于字符串2则返回值大于0;如果字符串1小于字符串2则返回值小于0。 | 本函数也可用于比较两个字符串常量,或比较数组和字符串常量。 |
测字符串长度函数 | strlen(字符数组名) | 测字符串的实际长度(不含字符串结束标志'\0' ) 并作为函数返回值。 | 无。 |
示例:
#include <stdio.h>
#include <string.h>
int main() {
// 字符串输出函数 puts
char c1[] = "hello\tworld";
puts(c1);
// 字符串输入函数 gets
char c2[15];
gets(c2);
puts(c2);
// 字符串连接函数 strcat
char c3[] = "hello ";
char c4[] = "world";
strcat(c3, c4);
puts(c3);
// 字符串拷贝函数 strcpy
char c5[15];
char c6[] = "hello world";
strcpy(c5, c6);
puts(c5);
// 字符串比较函数 strcmp
char c7[] = "hello";
char c8[] = "world";
int r = strcmp(c7, c8);
if (r == 0) {
puts("c7=c8");
} else if (r > 0) {
puts("c7>c8");
} else if (r < 0) {
puts("c7<c8");
}
// 测字符串长度函数 strlen
char c9[] = "hello world";
int len = strlen(c9);
printf("len=%d", len);
}