为了解决比较复杂的问题, 本章介绍 C语言提供的
一种最简单的构造类型 ──数组 。
6.1 1维数组的定义和引用
6.2 2维数组的定义和引用
6.3 字符数组与字符串
[Return]
第 6章 数 组
6.1 1维数组的定义和引用
6.1.1 1维数组的定义
6.1.2 1维数组元素的引用
6.1.3 1维数组元素的初始化
6.1.4 1维数组应用举例
[Return]
6.1.1 1维数组的定义
[案例 6.1] 从键盘上任意输入 10个整数, 要求按从小到大的顺序
在屏幕上显示出来 。
排序的方法有很多, 本题采用冒泡法 。
冒泡法的基本思想,通过相邻两个数之间的比较和交换, 使排序
码 ( 数值 ) 较小的数逐渐从底部移向顶部, 排序码较大的数逐渐从顶
部移向底部 。 就像水底的气泡一样逐渐向上冒, 故而得名 。
由 A[n]~A[1]组成的 n个数据, 进行冒泡排序的过程可以描述为:
( 1) 首先将相邻的 A[n]与 A[n-1]进行比较, 如果 A[n]的值小于
A[n-1]的值, 则交换两者的位置, 使较小的上浮, 较大的下沉;接着
比较 A[n-1]与 A[n-2],同样使小的上浮, 大的下沉 。 依此类推, 直到
比较完 A[2]和 A[1]后, A[1]为具有最小排序码 ( 数值 ) 的元素, 称第
一趟排序结束 。
( 2) 然后在 A[n]~A[2]区间内, 进行第二趟排序, 使剩余元素中
排序码最小的元素上浮到 A[2];重复进行 n-1趟后, 整个排序过程结
束 。
/*案例代码文件名,AL6_1.C*/
/*功能:从键盘上任意输入 n个整数, 用冒泡法按从小到大地排序,
并在屏幕上显示出来 。 */
#include "stdio.h"
#define NUM 10 /*定义符号常量 ( 数据个数 N) */
main()
{ int data[NUM]; /*定义 1个 1维整型数组 data*/
int i,j,temp; /*定义循环变量和临时变量 */
clrscr(); /*库函数 clrscr():清屏 */
printf("Please input 10 numbers:\n");
for(i=0; i<NUM; i++)
scanf("%d",&data[i]);
/*冒泡法排序 */
for(i=0; i<NUM-1; i++) /*外循环:控制比较趟数 */
for(j=NUM-1; j>i; j--) /*内循环:进行每趟比较 */
if(data[j]<data[j-1]) /*如果 data[j]大于 data[j-1],交换两者的位置 */
{temp=data[j];
data[j]=data[j-1];
data[j-1]=temp;
};
/*输出排序后的数据 */
printf("\nthe result of sort:\n");
for(i=0; i<NUM; i++)
printf("%d ",data[i]);
getch(); /*等待键盘输入任一字符, 目的使程序暂停 */
}
[程序演示 ]
数组同变量一样, 也必须先定义, 后使用 。
1维数组是只有 1个下标的数组, 定义形式如下:
数据类型 数组名 [常量表达式 ][,数组名 2[常量表达式
2]……] ;
( 1), 数据类型, 是指数组元素的数据类型 。
( 2) 数组名, 与变量名一样, 必须遵循标识符命名规
则 。
( 3)“常量表达式”必须用方括号括起来,指的是数
组的元素个数(又称数组长度),它是一个整型值,其中可
以包含常数和符号常量,但不能包含变量。
注意, C语言中不允许动态定义数组 。
特别说明,在数组定义时,, 常量表达式, 外的方
括号;以及元素引用时,, 下标表达式, 外的方括号,
都是 C语言语法规则所要求的, 不是本书所约定的可选项
的描述符号 !
( 4) 数组元素的下标, 是元素相对于数组起始地址
的偏移量, 所以从 0开始顺序编号 。
( 5) 数组名中存放的是一个地址常量, 它代表整个
数组的首地址 。 同一数组中的所有元素, 按其下标的顺
序占用一段连续的存储单元 。
[Return]
6.1.2 数组元素的引用
引用数组中的任意一个元素的形式:
数组名 [下标表达式 ]
1., 下标表达式, 可以是任何非负整型数据, 取值
范围是 0~( 元素个数 -1) 。
特别强调,在运行 C语言程序过程中, 系统并不自动
检验数组元素的下标是否越界 。 因此在编写程序时, 保
证数组下标不越界是十分重要的 。
2,1个数组元素, 实质上就是 1个变量, 它具有和相
同类型单个变量一样的属性, 可以对它进行赋值和参与
各种运算 。
3,在 C语言中, 数组作为 1个整体, 不能参加数据运
算, 只能对单个的元素进行处理 。
[Return]
6.1.3 1维数组元素的初始化
初始化格式,
数据类型 数组名 [常量表达式 ]= {初值表 }
( 1) 如果对数组的全部元素赋以初值, 定义时可以
不指定数组长度 ( 系统根据初值个数自动确定 ) 。 如果
被定义数组的长度, 与初值个数不同, 则数组长度不能
省略 。
( 2), 初值表, 中的初值个数, 可以少于元素个数,
即允许只给部分元素赋初值 。
( 3) 根据存储类型的不同, 数组有静态数组 ( static)
和动态数组 ( auto) 之分;根据定义的位置不同, 数组有
内部数组 ( 在函数内部定义的数组 ) 和外部数组 ( 在函
数外部定义的数组 ) 之分 。
[Return]
6.1.4 1维数组应用举例
[案例 6.2] 已知某课程的平时、实习、测验和期末成绩,求该课
程的总评成绩。其中平时、实习、测验和期末分别占 10%,20%、
20%,50%。
/*案例代码文件名,AL6_2.C*/
/*功能:从键盘上循环输入某课程的平时、实习、测验和期末成绩,
按 10%,20%,20%,50%的比例计算总评成绩,并在屏幕上显示
出来。按空格键继续循环,其他键终止循环。 */
#include,stdio.h”
main()
{ int i=1,j;
char con_key=?\x20?; /* ?\x20? 空格键的 ASCII码 */
float score[5],ratio[4]={0.1,0.2,0.2,0.5}; /*定义成绩,比例系数数组 */
while(con_key=='\x20')
while(con_key=='\x20')
{clrscr();
printf("输入第 %2d个学生的成绩 \n",i++);
printf("平时 实习 测验 期末成绩 \n");
score[4]=0; /* score[4]:存储总评成绩 */
for(j=0; j<4; j++)
{scanf("%f",&score[j]);
score[4] += score[j] * ratio[j];
}
printf("总评成绩为,%6.1f\n",score[4]);
printf("\n按空格键继续,其它键退出 ");
con_key=getch(); /*getch()函数等待从键盘上输入一个字符 */
}
}
[程序演示 ]
[Return]
6.2 2维数组的定义和引用
6.2.1 2维数组的定义
6.2.2 2维数组元素的引用
6.2.3 2维数组元素的初始化
6.2.4 2维数组应用举例
[Return]
[案例 6.3] 给一个 2* 3的 2维数组各元素赋值, 并输出全部元素的值 。
/*案例代码文件名,AL6_3.C*/
/*功能:从键盘上给 2* 3数组赋值, 并在屏幕上显示出来 。 */
#define Row 2
#define Col 3
#include "stdio.h"
main()
{ int i,j,array[Row][Col]; /*定义 1个 2行 3列的 2维数组 array*/
for(i=0; i<Row; i++) /*外循环:控制 2维数组的行 */
for(j=0; j<Col; j++) /*内循环:控制 2维数组的列 */
{printf("please input array[%2d][%2d]:",i,j);
scanf("%d",&array[i][j]); /*从键盘输入 a[i][j]的值 */
}
printf("\n");
/*输出 2维数组 array*/
for(i=0;i<Row;i++)
6.2.1 2维数组的定义
{ for(j=0;j<Col;j++)
printf("%d\t",array[i][j]); /*将 a[i][j]的值显示在屏幕上 */
printf("\n");
}
getch();
} [程序演示 ]
2维数组的定义方式如下:
数据类型 数组名 [行常量表达式 ][列常量表达式 ][,
数组名 2[行常量表达式 2][列常量表达式 2]……];
1.数组元素在内存中的排列顺序为“按行存放”,即
先顺序存放第一行的元素,再存放第二行,以此类推。
2,设有一个 m*n的数组 x,则第 i行第 j列的元素 x[i][j]在
数组中的位置为,i*n+j( 注意,行号、列号均从 0开始计
数)。
3,可以把 2维数组看作是一种特殊的 1维数组:它
的元素又是一个 1维数组 。
例如, 对 x[3][2],可以把 x看作是一个 1维数组, 它
有 3个元素,x[0],x[1],x[2],每个元素又是一个包含 2
个元素的 1维数组, 如图 6-4所示 。 即把 x[0],x[1],x[2]
看作是 3个 1维数组的名字 。
[Return]
6.2.2 2维数组元素的引用
引用 2维数组元素的形式为:
数组名 [行下标表达式 ][列下标表达式 ]
1., 行下标表达式, 和, 列下标表达式,, 都应是
整型表达式或符号常量 。
2., 行下标表达式, 和, 列下标表达式, 的值, 都
应在已定义数组大小的范围内 。 假设有数组 x[3][4],则可
用的行下标范围为 0~2,列下标范围为 0~3。
3,对基本数据类型的变量所能进行的操作, 也都适
合于相同数据类型的 2维数组元素 。
[Return]
6.2.3 2维数组元素的初始化
1,按行赋初值
数据类型 数组名 [行常量表达式 ][列常量表达式 ]= {{第 0行初值
表 },{第 1行初值表 },……, {最后 1行初值表 }};
赋值规则:将, 第 0行初值表, 中的数据, 依次赋给第 0行中各元
素;将, 第 1行初值表, 中的数据, 依次赋给第 1行各元素;以此类
推 。
2,按 2维数组在内存中的排列顺序给各元素赋初值
数据类型 数组名 [行常量表达式 ][列常量表达式 ]= {初值表 };
赋值规则:按 2维数组在内存中的排列顺序, 将初值表中的数据,
依次赋给各元素 。
如果对全部元素都赋初值,则“行数”可以省略。 注意,只能
省略“行数”。
[Return]
6.2.4 2维数组应用举例
[案例 6.4] 有 M个学生,学习 N门课程,已知所有学生的各科成绩,
编程:分别求每个学生的平均成绩和每门课程的平均成绩。
/*案例代码文件名,AL6_4.C*/
/*功能:计算个人平均成绩与各科平均成绩,并在屏幕上显示出来。 */
#define NUM_std 5 /*定义符号常量人数为 5*/
#define NUM_course 4 /*定义符号常量课程为 4*/
#include "stdio.h"
main()
{ int i,j;
static float score[NUM_std+1][NUM_course+1]={{78,85,83,65},
{88,91,89,93},{72,65,54,75},
{86,88,75,60},{69,60,50,72}};
for(i=0;i<NUM_std;i++)
{for(j=0;j<NUM_course;j++)
{ score[i][NUM_course] += score[i][j];/*求第 i个人的总成绩 */
score[NUM_std][j] += score[i][j];/*求第 j门课的总成绩 */
}
score[i][NUM_course] /= NUM_course;/*求第 i个人的平均成绩 */
}
for(j=0;j<NUM_course;j++)
score[NUM_std][j] /= NUM_std; /*求第 j门课的平均成绩 */
clrscr();
/*输出表头 */
printf("学生编号 课程 1 课程 2 课程 3 课程 4 个人平均 \n");
/*输出每个学生的各科成绩和平均成绩 */
for(i=0;i<NUM_std;i++)
{ printf("学生 %d\t",i+1);
for(j=0;j<NUM_course+1;j++)
printf("%6.1f\t",score[i][j]);
printf("\n");
}
/*输出 1条短划线 */
for(j=0;j<8*(NUM_course+2);j++)
printf("-");
printf("\n课程平均 ");
/*输出每门课程的平均成绩 */
for(j=0;j<NUM_course;j++)
printf("%6.1f\t",score[NUM_std][j]);
printf("\n");
getch();
} [程序演示 ]
[Return]
6.3 字符数组与字符串
6.3.1 字符数组的逐个字符操作
6.3.2 字符数组的整体操作
6.3.3 常用的字符串处理函数
[Return]
6.3.1 字符数组的逐个字符操作
[案例 6.5]从键盘输入一个字符串, 回车键结束, 并将字符串在屏幕上输出 。
/*案例代码文件名,AL6_5.C*/
main()
{int i;
static char str[80];
clrscr();
for(i=0;i<80;i++)
{ str[i]=getch(); /*逐次给数组元素 str[i]赋值, 但不回显在屏幕上 */
printf("*"); /*以星号代替输入字符的个数 */
if(str[i]=='\x0d') break;/*若输入回车则终止循环 */
}
i=0;
while(str[i]!='\x0d')
printf("%c",str[i++]); /*逐次输出字符数组的各个元素 */
printf("\n");
getch(); /*程序暂停 */
} [程序演示 ]
1,字符数组的定义
1维字符数组, 用于存储和处理 1个字符串, 其定义格式
与 1维数值数组一样 。
2维字符数组, 用于同时存储和处理多个字符串, 其定义
格式与 2维数值数组一样 。
2,字符数组的初始化
字符数组的初始化, 可以通过为每个数组元素指定初值
字符来实现 。
3,字符数组的引用
字符数组的逐个字符引用, 与引用数值数组元素类似 。
( 1) 字符数组的输入
除了可以通过初始化使字符数组各元素得到初值外,
也可以使用 getchar()或 scanf()函数输入字符 。
例如:
char str[10];
……
for(i=0; i<10; i++)
{ scanf("%c",&str[i]);
fflush(stdin); /*清除键盘输入缓冲区 */
}
……
( 2) 字符数组的输出
字符数组的输出, 可以用 putchar()或 printf()函数 。
例如:
char str[10]="c language";
……
for(i=0; i<10; i++) printf("%c",str[i]);
printf("\n");
……
注意,逐个字符输入, 输出时, 要指出元素的下标,
而且使用, %c”格式符 。 另外, 从键盘上输入字符时, 无
需输入字符的定界符 ──单引号;输出时, 系统也不输出
字符的定界符 。
[Return]
6.3.2 字符数组的整体操作
[案例 6.6] 字符数组的整体输入与输出 。
/*案例代码文件名,AL6_6.C*/
/*功能:将 2维字符数组进行初始化, 并在屏幕上输出 */
main()
{ int i;
char name[5][9]={"张三山 ","李四季 ","王五魁 ","刘六顺 ","赵七巧 "};
for(i=0;i<5;i++)
printf("\n%s\t",name[i]); /*name[i]代表该行数组元素的首地址 */
getch();
} [程序演示 ]
1.字符串及其结束标志
所谓字符串,是指若干有效字符的序列。 C语言中的字符串,可以包
括字母、数字、专用字符、转义字符等。
C语言规定:以‘ \0?作为字符串结束标志(‘ \0?代表 ASCII码为 0的字
符,表示一个“空操作”,只起一个标志作用)。因此可以对字符数组采
用另一种方式进行操作了 ──字符数组的整体操作 。
注意,由于系统在存储字符串常量时, 会在串尾自动加上 1个结束
标志, 所以无需人为地再加 1个 。
另外, 由于结束标志也要在字符数组中占用一个元素的存储空间,
因此在说明字符数组长度时, 至少为字符串所需长度加 1。
2,字符数组的整体初始化
字符串设置了结束标志以后, 对字符数组的初始化, 就可以用字符
串常量来初始化字符数组 。
3,字符数组的整体引用
( 1) 字符串的输入
除了可以通过初始化使字符数组各元素得到初值外, 也可以使用
scanf()函数输入字符串 。
( 2) 字符串的输出
printf()函数, 不仅可以逐个输出字符数组元素, 还可以整体输出存
放在字符数组中的字符串 。
[Return]
6.3.3 常用的字符串处理函数
字符串标准函数的原型在头文件 string.h中 。
1,输入字符串 ──gets()函数
( 1) 调用方式,gets(字符数组 )
( 2) 函数功能:从标准输入设备 (stdin)──键盘上, 读取
1个字符串 ( 可以包含空格 ), 并将其存储到字符数组中去 。
( 3) 使用说明
1) gets()读取的字符串, 其长度没有限制, 编程者要保
证字符数组有足够大的空间, 存放输入的字符串 。
2) 该函数输入的字符串中允许包含空格, 而 scanf()函数
不允许 。
2,输出字符串 ──puts()函数
( 1) 调用方式,puts(字符数组 )
( 2) 函数功能:把字符数组中所存放的字符串, 输出
到标准输出设备中去, 并用 ‘ \n?取代字符串的结束标志 ‘ \0?。
所以用 puts()函数输出字符串时, 不要求另加换行符 。
( 3) 使用说明
1)字符串中允许包含转义字符, 输出时产生一个控制操
作 。
2)该函数一次只能输出一个字符串, 而 printf()函数也能
用来输出字符串, 且一次能输出多个 。
3,字符串比较 ──strcmp()函数
( 1) 调用方式,strcmp(字符串 1,字符串 2)
其中, 字符串, 可以是串常量, 也可以是 1维字符数组 。
( 2) 函数功能:比较两个字符串的大小 。
如果,字符串 1=字符串 2,函数返回值等于 0;
字符串 1<字符串 2,函数返回值负整数;
字符串 1>字符串 2,函数返回值正整数 。
( 3) 使用说明
1) 如果一个字符串是另一个字符串从头开始的子串, 则母串为大 。
2) 不能使用关系运算符, ==, 来比较两个字符串, 只能用
strcmp() 函数来处理 。
[案例 6.7] gets函数和 strcmp函数的应用 。
/*案例代码文件名,AL6_7.C*/
/*功能:简单密码检测程序 */
#include "stdio.h"
main()
{char pass_str[80]; /*定义字符数组 passstr*/
int i=0;
/*检验密码 */
while(1)
{clrscr();
printf("请输入密码 \n");
gets(pass_str); /*输入密码 */
if(strcmp(pass_str,“password”)!=0) /*口令错 */
printf("口令错误, 按任意键继续 ");
else
break; /*输入正确的密码, 中止循环 */
getch();
i++;
if(i==3) exit(0); /*输入三次错误的密码, 退出程序 */
}
/*输入正确密码所进入的程序段 */
}
[程序演示 ]
4,拷贝字符串 ──strcpy()函数
( 1) 调用方式,strcpy(字符数组,字符串 )
其中, 字符串, 可以是串常量, 也可以是字符数组 。
( 2) 函数功能:将, 字符串, 完整地复制到, 字符数
组, 中, 字符数组中原有内容被覆盖 。
( 3) 使用说明
1) 字符数组必须定义得足够大, 以便容纳复制过来的
字符串 。 复制时, 连同结束标志 '\0'一起复制 。
2) 不能用赋值运算符, =, 将一个字符串直接赋值给
一个字符数组, 只能用 strcpy()函数来处理 。
5,连接字符串 ──strcat()函数
( 1) 调用方式,strcat(字符数组,字符串 )
( 2) 函数功能:把, 字符串, 连接到, 字符数组, 中
的字符串尾端, 并存储于, 字符数组, 中 。, 字符数组,
中原来的结束标志, 被, 字符串, 的第一个字符覆盖, 而
,字符串, 在操作中未被修改 。
( 3) 使用说明
1) 由于没有边界检查, 编程者要注意保证, 字符数组,
定义得足够大, 以便容纳连接后的目标字符串;否则, 会
因长度不够而产生问题 。
2) 连接前两个字符串都有结束标志 '\0',连接后, 字
符数组, 中存储的字符串的结束标志 '\0'被舍弃, 只在目标
串的最后保留一个 '\0'。
6,求字符串长度 ──strlen()函数 ( len是 length的缩写 )
( 1) 调用方式,strlen(字符串 )
( 2) 函数功能:求字符串 ( 常量或字符数组 ) 的实际长度
( 不包含结束标志 ) 。
7,将字符串中大写字母转换成小写 ──strlwr()函数
( 1) 调用方式,strlwr(字符串 )
( 2) 函数功能:将字符串中的大写字母转换成小写, 其它字
符 ( 包括小写字母和非字母字符 ) 不转换 。
8,将字符串中小写字母转换成大写 ──strupr()函数
( 1) 调用方式,strupr(字符串 )
( 2) 函数功能:将字符串中小写字母转换成大写, 其它字符
( 包括大写字母和非字母字符 ) 不转换 。
[Return]