制 作:方 斌
C语言程序设计教程郧阳师范高等专科学校计算机科学系方 斌 制作制 作:方 斌为了解决比较复杂的问题,本章介绍 C语言提供的一种最简单的构造类型 ── 数组 。
7.1 一维数组的定义和引用
7.2 二维数组的定义和引用
7.3 字符数组与字符串第 7章 数 组制 作:方 斌
7.1 一维数组的定义和引用
7.1.1 一维数组的定义
7.1.2 一维数组元素的引用
7.1.3 一维数组元素的初始化
7.1.4 一维数组应用举例制 作:方 斌
7.1.1 一维数组的定义数组:数组是一组有序数据的集合,数组中每一个元素的类型相同。用数组名和下标来唯一确定数组中的元素。
一、一维数组的定义定义方式,类型说明符 数组名 [下标常量表达式 ]
例,int a[10]
定义一个数组,数组名 a,有 10个元素,每个元素的类型均为 int型。
这 10个元素分别是,a[0],a[1],a[2],a[3],a[4],....,a[8]、
a[9]。
注意:
( 1) C语言中,数组下标从 0开始,
( 2) C语言不允许对数组的大小做动态定义,如:
制 作:方 斌
int n;
scanf("%d",&n);
int a[n]; ×
因为,在编译时,C编译器根据已知数组大小分配内存。
二、数组元素的引用
C语言规定,不能引用整个数组,只能逐个引用元素,元素引用方式:
数组名 [下标 ]
例、
a[0] = a[5] + a[7] - a[2*3]
制 作:方 斌
[例 7.1] 使数组元素 a[0]~ a[9]的值为 0~ 9,然后逆序输出。
main()
{
int i,a[10];
for (i=0;i<=9;i++) a[i] = i;
for(i=9;i>=0; i--)
printf("%d ",a[i]);
}
运行输出:
9 8 7 6 5 4 3 2 1 0
制 作:方 斌三、一维数组的初始化初始化:在定义时指定初始值,编译器把初值赋给数组变量。
赋值:使用赋值语句,在程序运行时把值赋给数组变量,如 a[0] = 2。
1、一般初始化,例、
static int a[10] = { 0,1,2,3,4,5,6,7,8,9};
int array[10] = {1,2,3,4,5,6,7,8,9,10};
2、部分元素初始化,例、
static int a[10] = {0,1,2,3,4};
仅前 5个元素赋初值,后 5个元素未指顶初值。
3、全部元素均初始化为 0,不允许简写。
static int a[10] = {0,0,0,0,0,0,0,0,0,0};
不能简写为:
static int a[10] = {0*10}; ×
制 作:方 斌注意:当程序不给数组指定初始值时,编译器作如下处理:
( 1)编译器自动把静态数组的各元素初始化为 0。
( 2)编译器不为动态数组自动指定初始值。
4、如果全部元素均指定初值,定义中可以省略元素的个数,例、
static int a[5] = {1,2,3,4,5};
可以写为:
static int a[ ] = {1,2,3,4,5}; √
四、一维数组程序举例
[例 7.2] 用数组来处理 Fibonicci数列的前 20项。
F1 = 1 n = 1
F2 = 1 n = 2
Fn = Fn-1 + Fn-2 n ≥ 3
制 作:方 斌程序:
main()
{
int i;
static int f[20] = {1,1}; /* f1,f2已知 */
for(i=2; i<20; i++) f[i] = f[i-1] + f[i-2];
for(i=0; i<20; i++)
{
if (i%5 == 0) printf("\n");
printf("%12d",f[i]);
}
}
制 作:方 斌
[例 7.3] 输入 10个数,用“起泡法”对 10个数排序(由小到大)。
“起泡法”算法:以六个数 9,8,5,4,2,0为例。
第 1趟比较 第 2趟比较第 1趟比较后,剩 5个数未排好序;两两比较 5次第 2趟比较后,剩 4个数未排好序;两两比较 4次第 3趟比较后,剩 3个数未排好序;两两比较 3次第 4趟比较后,剩 2个数未排好序;两两比较 2次第 5趟比较后,全部排好序;两两比较 1次算法结论:对于 n个数的排序,需进行 n-1趟比较,第 j趟比较需进行 n-j次两两比较。
制 作:方 斌程序流程图:(用两层嵌套循环实现)
程序:设需排序的数有 10个,定义数组大小为 11,使用 a[1]~ a[10]存放 10个数,
a[0]不用。
制 作:方 斌
main()
{
int a[11]; /* 用 a[1]~ a[10],a[0]不用 */
int i,j,t; /* i,j作循环变量,t作两两比较的临时变量 */
printf("input 10 numbers:\n");
for(i=1;i<11;i++) scanf("%d",&a[i]); /* 输入 10个整数 */
printf("\n");
for(j=1;j<=9;j++) /* 第 j趟比较 */
for(i=1;i<=10-j; i++) /* 第 j趟中两两比较 10-j次 */
if (a[i] > a[i+1]) /* 交换大小 */
{ t = a[i]; a[i] = a[i+1]; a[i+1] = t; }
printf("the sorted numbers:\n");
for(i=1;i<11;i++) printf("%d",a[i]);
}
制 作:方 斌
[案例 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) 数组名中存放的是一个地址常量,它代表整个数组的首地址 。 同一数组中的所有元素,按其下标的顺序占用一段连续的存储单元 。
制 作:方 斌引用数组中的任意一个元素的形式:
数组名 [下标表达式 ]
1.,下标表达式,可以是任何非负整型数据,取值范围是 0~
( 元素个数 -1) 。
特别强调,在运行 C语言程序过程中,系统并不自动检验数组元素的下标是否越界 。 因此在编写程序时,保证数组下标不越界是十分重要的 。
2,1个数组元素,实质上就是 1个变量,它具有和相同类型单个变量一样的属性,可以对它进行赋值和参与各种运算 。
3,在 C语言中,数组作为 1个整体,不能参加数据运算,只能对单个的元素进行处理 。
制 作:方 斌初始化格式,
数据类型 数组名 [常量表达式 ]= {初值表 }
( 1) 如果对数组的全部元素赋以初值,定义时可以不指定数组长度 ( 系统根据初值个数自动确定 ) 。 如果被定义数组的长度,与初值个数不同,则数组长度不能省略 。
( 2),初值表,中的初值个数,可以少于元素个数,即允许只给部分元素赋初值 。
( 3) 根据存储类型的不同,数组有静态数组 ( static) 和动态数组 ( auto) 之分;根据定义的位置不同,数组有内部数组 ( 在函数内部定义的数组 ) 和外部数组 ( 在函数外部定义的数组 ) 之分 。
制 作:方 斌
[案例 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];
float ratio[4]={0.1,0.2,0.2,0.5}; /*定义成绩,比例系数数组 */
制 作:方 斌
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()函数等待从键盘上输入一个字符 */
}
}
制 作:方 斌
7.2.2维数组的定义和引用
7.2.1 2维数组的定义
7.2.2 2维数组元素的引用
7.2.3 2维数组元素的初始化
7.2.4 2维数组应用举例制 作:方 斌
7.2.1 二维数组的定义一、二维数组的定义一般形式:
类型说明符 数组名 [常量表达式 ][常量表达式 ]
例、
float a[3][4]; a为 3× 4(3行 4列 )的数组
float b[5][10]; b为 5× 10(5行 10列 )的数组二维数组的理解:
二维数组 a[3][4]理解为:有三个元素 a[0],a[1],a[2],
每一个元素是一个包含 4个元素的数组。
制 作:方 斌二维数组的元素在内存中的存放循序:
按行存放,即:先顺序存放第一行的元素,再存放第二行的元素。(最右边的下标变化最快,第一维的下标变化最慢)。
三维数组,float a[2][3][4]在内存中的存放顺序:
a[0][0][0]→ a[0][0][1]→ a[0][0][2]→ a[0][0][3]→
a[0][1][0]→ a[0][1][1]→ a[0][1][2]→ a[0][1][3]→
a[0][2][0]→ a[0][2][1]→ a[0][2][2]→ a[0][2][3]→
a[1][0][0]→ a[1][0][1]→ a[1][0][2]→ a[1][0][3]→
a[1][1][0]→ a[1][1][1]→ a[1][1][2]→ a[1][1][3]→
a[1][2][0]→ a[1][2][1]→ a[1][2][2]→ a[1][2][3]
制 作:方 斌二、二维数组中元素的引用用数组名和下标引用元素。例、
float a[2][3]; 有 6个元素,按如下方式引用各元素:
a[0][0],a[0][1],a[0][2],a[1][0],a[1][1],a[1][2]
注意:数组 float a[2][3]中无元素 a[2][3]。(下标从 0始)
三、二维数组的初始化
1、分行赋值,如、
static int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
2、全部数据写在一个大括号内,如、
static int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
3、部分元素赋值,如、
static int a[3][4] = {{1},{5},{9}};
仅对 a[0][0],a[1][0],a[2][0]赋值,其余元素未赋值(对于静态数组,
编译器自动为未赋值元素指定初值 0;对于动态数组,未赋值元素的初值是随机的)。
制 作:方 斌
4、如果对全部元素赋初值,则第一维的长度可以不指定,但必须指定第二维的长度。例、
static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
与下面定义等价:
static int a[ ][4]={1,2,3,4,5,6,7,8,9,10,11,12};
四、二维数组程序举例
[例 7.4] 将一个二维数组行和列交换,存到另一个二维数组中。例如、
教材 p127~ 128还列举了一些部分元素赋初值的例子,请自行阅读。
算法,b[j][i] = a[i][j]
制 作:方 斌程序:
main()
{
static int a[2][3] = {{1,2,3},{4,5,6}};
static int b[3][2],i,j;
printf("array a:\n");
for(i=0;i<=1;i++) /* 0~ 1行 */
{
for(j=0;j<=2;j++) /* 0~ 2列 */
{
printf("%5d",a[i][j]);
b[j][i] = a[i][j]; /* 行、列交换 */
}
printf("\n"); /*输出一行后换行 */
}
printf("array b:\n");
for(i=0;i<=2;i++)
{
for(j=0;j<=1;j++)
printf("%5d",b[i][j]);
printf("\n"); /*输出一行后换行 */
}
}
制 作:方 斌
[例 7.5] 有一个 3× 4的矩阵,要求编程序以求出其中值最大的那个元素的值及其所在的行号和列号。
算法:首先把第一个元素 a[0][0]作为临时最大值 max,然后把临时最大值 max与每一个元素 a[i][j]进行比较,若 a[i][j]>max,把 a[i][j]作为新的临时最大值,并记录下其下标 i和 j。当全部元素比较完后,max是整个矩阵全部元素的最大值。
流程:
制 作:方 斌程序:
main()
{
int i,j,row=0,colum=0,max;
static int a[3][4]={{1,2,3,4},{9,8,7,6},{-10,10,-5,2}};
max = a[0][0];
for(i=0; i<=2; i++) /* 用两重循环遍历全部元素 */
for(j=0; j<=3; j++)
if (a[i][j] > max )
{ max = a[i][j];
row = i;
colum = j;
}
printf("max=%d,row=%d,colum=%d\n",max,row,colum);
}
注意:本例中得到的行列值从 0始。
制 作:方 斌[案例 1] 给一个 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");
for(i=0;i<Row;i++) /*输出 2维数组 array*/
{ for(j=0;j<Col;j++)
printf("%d\t",array[i][j]); /*将 a[i][j]的值显示在屏幕上 */
printf("\n");
}
getch();
}
制 作:方 斌复习:
二维数组的定义方式如下:
数据类型 数组名 [行常量表达式 ][列常量表达式 ][,数组名
2[行常量表达式 2][列常量表达式 2]……];
1.数组元素在内存中的排列顺序为“按行存放”,即先顺序存放第一行的元素,再存放第二行,以此类推。
2,设有一个 m*n的数组 x,则第 i行第 j列的元素 x[i][j]在数组中的位置为,i*n+j( 注意,行号、列号均从 0开始计数)。
3,可以把二维数组看作是一种特殊的一维数组:它的元素又是一个 1维数组 。
例如,对 x[3][2],可以把 x看作是一个一维数组,它有 3个元素:
x[0],x[1],x[2],每个元素又是一个包含 2个元素的 1维数组,即把
x[0],x[1],x[2]看作是 3个一维数组的名字 。
制 作:方 斌引用二维数组元素的形式为:
数组名 [行下标表达式 ][列下标表达式 ]
1.,行下标表达式,和,列下标表达式,,都应是整型表达式或符号常量 。
2.,行下标表达式,和,列下标表达式,的值,都应在已定义数组大小的范围内 。 假设有数组 x[3][4],则可用的行下标范围为
0~2,列下标范围为 0~3。
3,对基本数据类型的变量所能进行的操作,也都适合于相同数据类型的 2维数组元素 。
制 作:方 斌二维数组元素的初始化
1,按行赋初值数据类型 数组名 [行常量表达式 ][列常量表达式 ]= {{第 0行初值表 },{第 1行初值表 },……,{最后 1行初值表 }};
赋值规则:将,第 0行初值表,中的数据,依次赋给第 0行中各元素;将,第 1行初值表,中的数据,依次赋给第 1行各元素;以此类推 。
2,按二维数组在内存中的排列顺序给各元素赋初值数据类型 数组名 [行常量表达式 ][列常量表达式 ]= {初值表 };
赋值规则:按 2维数组在内存中的排列顺序,将初值表中的数据,
依次赋给各元素 。
如果对全部元素都赋初值,则“行数”可以省略。 注意,只能省略“行数”。
制 作:方 斌二维数组应用 案 例
[案例 2] 有 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,0},
{88,91,89,93,0},{72,65,54,75,0},
{86,88,75,60,0},{69,60,50,72,0},
{0,0,0,0,0}};
制 作:方 斌
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课程平均 ");
/*输出每门课程的平均成绩 */
printf(“\t”);
for(j=0;j<NUM_course;j++)
printf(“%6.1f\t",score[NUM_std][j]);
printf("\n");
getch();
}
制 作:方 斌
78 85 83 65 0
88 91 89 93 0
72 65 54 75 0
86 88 75 60 0
69 60 50 72 0
0 0 0 0 0
初始状态,0 1 2 3 4
0
1
2
3
4
5
for(i=0; i<NUM_std; i++) /* NUM_stu=5 */
{for(j=0; j<NUM_course; j++) /* NUM_course=4 */
{
}
}
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门课的平均成绩 */
311
361
266
309
251
393 389 351 365
77.8
90.3
66.5
77.3
62.8
78.6 77.8 70.2 73.0
制 作:方 斌输出结果,
学生编号 课程 1 课程 2 课程 3 课程 4 个人平均学生 1 78 85 83 65 77.8
学生 2 88 91 89 93 90.3
学生 3 72 65 54 75 66.5
学生 4 86 88 75 60 77.3
学生 5 69 60 50 72 62.8
----------------------------------------------------
课程平均
78.6 77.8 70.2 73.0
制 作:方 斌
7.3 字符数组与字符串
7.3.1 字符数组的定义
7.3.2 字符数组的初始化
7.3.3 字符数组的引用
7.3.4 字符串和字符串结束标志
7.3.5 字符数组的输入和输出
7.3.6 常用的字符串处理函数制 作:方 斌字符数组:存放字符数据的数组,每一个元素存放一个字符。
一、程序中定义字符数组例、
char c[10]; /* 定义 c为字符数组,包含 10个元素 */
c[0]='I'; c[1]=' '; c[2]='a'; c[3]='m'; c[4]=' ';
c[5]='h'; c[6]='a'; c[7]='p'; c[8]='p'; c[9]='y';
注意:
字符型与整型可以通用,但有区别:
char c[10]; /* 在内存中占 10字节 */
int c[10]; /* 在内存中占 20字节 */
制 作:方 斌二、字符数组的初始化
1、逐个元素初始化
static char c[10] = {'I',' ','a','m',' ','h','a','p','p','y'};
2、初始化数据少于数组长度,多余元素自动为“空”( '\0',二进制 0)。
static char c[10] = {'c',' ','p','r','o','g','r','a','m'}; /* 9 */
3、指定初值时,若未指定数组长度,则长度等于初值个数。
static char c[ ] = {'I',' ','a','m',' ','h','a','p','p','y'};
三、字符数组的引用引用一个元素,得到一个字符。
制 作:方 斌
[例 7.6] 输出一个字符串。
main()
{static char c[10]={'I',' ','a','m','
','a',' ','b','o','y'};
int i;
for(i=0;i<10;i++)
printf("%c",c[i]);
printf("\n");
}
输出结果:
I am a boy
制 作:方 斌
[例 7.7] 输出一个钻石图形。
main()
{static char diamond[][5]={{' ',' ','*'},
{' ','*',' ','*'},
{'*',' ',' ',' ','*'},
{' ','*',' ','*'},
{' ',' ','*'} };
int i,j;
for(i=0;i<5;i++)
{ for(j=0;j<5;j++)
printf("%c",diamond[i][j]);
printf("\n");
}
}
输出形状:
制 作:方 斌四、字符串字符串例子,"I am a boy"
1,C语言中,字符串作为字符数组处理。字符数组可以用字符串来初始化,例、
static char c[] = {"I am happy"};
也可以这样初始化:(不要大括号)
static char c[] = "I am happy";
2、字符串在存储时,系统自动在其后加上结束标志 '\0'(占一字节,其值为二进制 0)。但字符数组并不要求其最后一个元素是 '\0',例、
static char c[] = {"China"};
static char c[5] = {"China"};
static char c[10] = {"China"};
制 作:方 斌五、字符数组的输入输出两种方法:
1、用,%c”格式符逐个输入输出。
2、用,%s”格式符按字符串输入输出。例、
static char c[6];
scanf("%s",c);
printf("%s",c);
注意:
( 1)输出时,遇 '\0'结束,且输出字符中不包含 '\0'。
( 2),%s”格式输出字符串时,printf()函数的输出项是字符数组名,
而不是元素名。
static char c[6] = "China";
printf("%s",c); printf("%c",c[0]);
printf("%s",c[0]); ×
制 作:方 斌
( 3),%s”格式输出时,即使数组长度大于字符串长度,
遇 '\0'也结束。例、
static char c[10] = {"China"};
printf("%s",c); /*只输出 5个字符 */
( 4),%s”格式输出时,若数组中包含一个以上 '\0',
遇第一个 '\0'时结束。
( 5)输入时,遇回车键结束,但获得的字符中不包含回车键本身( 0x0D,0x0A),而是在字符串末尾添 '\0'。
因此,定义的字符数组必须有足够的长度,以容纳所输入的字符。(如,输入 5个字符,定义的字符数组至少应有 6个元素)。
制 作:方 斌
( 6)一个 scanf函数输入多个字符串,输入时以“空格”键作为字符串间的分隔。例、
static char str1[5],str2[5],str3[5];
scanf("%s%s%s",str1,str2,str3);
输入数据,How are you?
str1,str2,str3获得的数据见右图。
例、
static char str[13];
scanf("%s",str);
输入,How are you?
结果:仅,How”被输入数组 str
如要想 str获得全部输入(包含空格及其以后的字符),程序应设计为:
static char c[13];
int i;
for(i=0;i<13;i++) c[i] = getchar();
制 作:方 斌
( 7) C语言中,数组名代表该数组的起始地址,因此,scanf()函数中不需要地址运算符 &。
例、
static char str[13];
scanf("%s",str);
scanf("%s",&str);
C语言中,数组名代表该数组的起始地址。如右图所示的数组 C,占 6个字节。
在程序中只写数组名 C,它就代表地址
2000。如
Printf(“%o”,c);
输出结果:
2000 /*数组 C在内存中的首地址 */ \0
a
n
i
h
C
2005
2004
2003
2002
2001
2000
C语言实际上是按字符数组名找到数组的起始地址,然后逐个输出一个个字符,直到遇到字符结束符 \0为止。其他类型的数组名其实也是该数组在内存中的起始地址 (基地址 )
制 作:方 斌
[案例 7.5]从键盘输入一个字符串,回车键结束,并将字符串在屏幕上输出 。
/*案例代码文件名,AL6_5.C*/
main()
{int i;
static char str[80];
clrscr();
for(i=0;i<80;i++)
{ str[i]=getchar(); /*逐次给数组元素 str[i]赋值,但不回显在屏幕上 */
printf("*"); /*以星号代替输入字符的个数 */
if(str[i]=='\x0d') break; /*若输入回车则终止循环 */
}
i=0;
while(str[i]!='\x0d')
printf("%c",str[i++]); /*逐次输出字符数组的各个元素 */
printf("\n");
getchar(); /*程序暂停 */
}
制 作:方 斌复习:
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”格式符 。 另外,从键盘上输入字符时,无需输入字符的定界符 ── 单引号;输出时,系统也不输出字符的定界符 。
制 作:方 斌7.3.2 字符数组的整体操作
[案例 7.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]代表该行数组元素的首地址 */
getchar();
}
1.字符串及其结束标志所谓字符串,是指若干有效字符的序列。 C语言中的字符串,可以包括字母、
数字、专用字符、转义字符等。
C语言规定:以‘ \0?作为字符串结束标志(‘ \0?代表 ASCII码为 0的字符,表示一个“空操作”,只起一个标志作用)。因此可以对字符数组采用另一种方式进行操作了 ── 字符数组的整体操作 。
制 作:方 斌注意,由于系统在存储字符串常量时,会在串尾自动加上 1个结束标志,所以无需人为地再加 1个 。
另外,由于结束标志也要在字符数组中占用一个元素的存储空间,
因此在说明字符数组长度时,至少为字符串所需长度加 1。
2,字符数组的整体初始化字符串设置了结束标志以后,对字符数组的初始化,就可以用字符串常量来初始化字符数组 。
3,字符数组的整体引用
( 1) 字符串的输入除了可以通过初始化使字符数组各元素得到初值外,也可以使用
scanf()函数输入字符串 。
( 2) 字符串的输出
printf()函数,不仅可以逐个输出字符数组元素,还可以整体输出存放在字符数组中的字符串 。
制 作:方 斌
7.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() 函数来处理 。
制 作:方 斌
[案例 7.7] gets函数和 strcmp函数的应用 。
/*案例代码文件名,AL6_7.C,功能:简单密码检测程序 */
#include "stdio.h"
main()
{char pass_str[80]; /*定义字符数组 pass_str*/
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) 函数功能:将字符串中小写字母转换成大写,其它字符
( 包括大写字母和非字母字符 ) 不转换 。
制 作:方 斌
[例 7.9] 输入三个字符串,并找出其中最大者。
分析:用 strcmp()函数比较字符串的大小。首先比较前两个,把较大者拷贝给字符数组变量 string(用 strcpy()函数拷贝),再比较 string和第三个字符串。
程序:设字符串最长为 19个字符。
#include "string.h" /* strcmp,strcpy函数均在 string.h中定义 */
main()
{
char string[20]; /* 存最大字符串 */
char str[3][20]; /* 三个字符串 */
int i;
for(i=0;i<3;i++) gets(str[i]); /* 输入三个字符串 */
if (strcmp(str[0],str[1]) > 0) strcpy(string,str[0]);
else strcpy(string,str[1]);
if (strcmp(str[2],string) > 0) strcpy(string,str[2]);
printf("\nthe largest string is,\n%s\n",string);
}
制 作:方 斌本章要求及作业数组是程序设计的常用数据结构,字符串(字符数组)在现代程序中也用得相当普遍,应掌握它们的意义和用法。
1、数组(一维和二维,下同)名、数组元素的概念。
2、数组的初始化方法。
3、数组元素在内存中的存储顺序。
4、数据排序算法。
5、字符串的特点(作为数组处理、最后一字节加 '\0')。
6、字符串处理函数的应用。
作业:
7.2,7.3,7.4,7.6,7.7,7.9,7.10,7.13
上机:
( 1)作业及熟悉 Turbo C在线帮助系统的用法。
C语言程序设计教程郧阳师范高等专科学校计算机科学系方 斌 制作制 作:方 斌为了解决比较复杂的问题,本章介绍 C语言提供的一种最简单的构造类型 ── 数组 。
7.1 一维数组的定义和引用
7.2 二维数组的定义和引用
7.3 字符数组与字符串第 7章 数 组制 作:方 斌
7.1 一维数组的定义和引用
7.1.1 一维数组的定义
7.1.2 一维数组元素的引用
7.1.3 一维数组元素的初始化
7.1.4 一维数组应用举例制 作:方 斌
7.1.1 一维数组的定义数组:数组是一组有序数据的集合,数组中每一个元素的类型相同。用数组名和下标来唯一确定数组中的元素。
一、一维数组的定义定义方式,类型说明符 数组名 [下标常量表达式 ]
例,int a[10]
定义一个数组,数组名 a,有 10个元素,每个元素的类型均为 int型。
这 10个元素分别是,a[0],a[1],a[2],a[3],a[4],....,a[8]、
a[9]。
注意:
( 1) C语言中,数组下标从 0开始,
( 2) C语言不允许对数组的大小做动态定义,如:
制 作:方 斌
int n;
scanf("%d",&n);
int a[n]; ×
因为,在编译时,C编译器根据已知数组大小分配内存。
二、数组元素的引用
C语言规定,不能引用整个数组,只能逐个引用元素,元素引用方式:
数组名 [下标 ]
例、
a[0] = a[5] + a[7] - a[2*3]
制 作:方 斌
[例 7.1] 使数组元素 a[0]~ a[9]的值为 0~ 9,然后逆序输出。
main()
{
int i,a[10];
for (i=0;i<=9;i++) a[i] = i;
for(i=9;i>=0; i--)
printf("%d ",a[i]);
}
运行输出:
9 8 7 6 5 4 3 2 1 0
制 作:方 斌三、一维数组的初始化初始化:在定义时指定初始值,编译器把初值赋给数组变量。
赋值:使用赋值语句,在程序运行时把值赋给数组变量,如 a[0] = 2。
1、一般初始化,例、
static int a[10] = { 0,1,2,3,4,5,6,7,8,9};
int array[10] = {1,2,3,4,5,6,7,8,9,10};
2、部分元素初始化,例、
static int a[10] = {0,1,2,3,4};
仅前 5个元素赋初值,后 5个元素未指顶初值。
3、全部元素均初始化为 0,不允许简写。
static int a[10] = {0,0,0,0,0,0,0,0,0,0};
不能简写为:
static int a[10] = {0*10}; ×
制 作:方 斌注意:当程序不给数组指定初始值时,编译器作如下处理:
( 1)编译器自动把静态数组的各元素初始化为 0。
( 2)编译器不为动态数组自动指定初始值。
4、如果全部元素均指定初值,定义中可以省略元素的个数,例、
static int a[5] = {1,2,3,4,5};
可以写为:
static int a[ ] = {1,2,3,4,5}; √
四、一维数组程序举例
[例 7.2] 用数组来处理 Fibonicci数列的前 20项。
F1 = 1 n = 1
F2 = 1 n = 2
Fn = Fn-1 + Fn-2 n ≥ 3
制 作:方 斌程序:
main()
{
int i;
static int f[20] = {1,1}; /* f1,f2已知 */
for(i=2; i<20; i++) f[i] = f[i-1] + f[i-2];
for(i=0; i<20; i++)
{
if (i%5 == 0) printf("\n");
printf("%12d",f[i]);
}
}
制 作:方 斌
[例 7.3] 输入 10个数,用“起泡法”对 10个数排序(由小到大)。
“起泡法”算法:以六个数 9,8,5,4,2,0为例。
第 1趟比较 第 2趟比较第 1趟比较后,剩 5个数未排好序;两两比较 5次第 2趟比较后,剩 4个数未排好序;两两比较 4次第 3趟比较后,剩 3个数未排好序;两两比较 3次第 4趟比较后,剩 2个数未排好序;两两比较 2次第 5趟比较后,全部排好序;两两比较 1次算法结论:对于 n个数的排序,需进行 n-1趟比较,第 j趟比较需进行 n-j次两两比较。
制 作:方 斌程序流程图:(用两层嵌套循环实现)
程序:设需排序的数有 10个,定义数组大小为 11,使用 a[1]~ a[10]存放 10个数,
a[0]不用。
制 作:方 斌
main()
{
int a[11]; /* 用 a[1]~ a[10],a[0]不用 */
int i,j,t; /* i,j作循环变量,t作两两比较的临时变量 */
printf("input 10 numbers:\n");
for(i=1;i<11;i++) scanf("%d",&a[i]); /* 输入 10个整数 */
printf("\n");
for(j=1;j<=9;j++) /* 第 j趟比较 */
for(i=1;i<=10-j; i++) /* 第 j趟中两两比较 10-j次 */
if (a[i] > a[i+1]) /* 交换大小 */
{ t = a[i]; a[i] = a[i+1]; a[i+1] = t; }
printf("the sorted numbers:\n");
for(i=1;i<11;i++) printf("%d",a[i]);
}
制 作:方 斌
[案例 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) 数组名中存放的是一个地址常量,它代表整个数组的首地址 。 同一数组中的所有元素,按其下标的顺序占用一段连续的存储单元 。
制 作:方 斌引用数组中的任意一个元素的形式:
数组名 [下标表达式 ]
1.,下标表达式,可以是任何非负整型数据,取值范围是 0~
( 元素个数 -1) 。
特别强调,在运行 C语言程序过程中,系统并不自动检验数组元素的下标是否越界 。 因此在编写程序时,保证数组下标不越界是十分重要的 。
2,1个数组元素,实质上就是 1个变量,它具有和相同类型单个变量一样的属性,可以对它进行赋值和参与各种运算 。
3,在 C语言中,数组作为 1个整体,不能参加数据运算,只能对单个的元素进行处理 。
制 作:方 斌初始化格式,
数据类型 数组名 [常量表达式 ]= {初值表 }
( 1) 如果对数组的全部元素赋以初值,定义时可以不指定数组长度 ( 系统根据初值个数自动确定 ) 。 如果被定义数组的长度,与初值个数不同,则数组长度不能省略 。
( 2),初值表,中的初值个数,可以少于元素个数,即允许只给部分元素赋初值 。
( 3) 根据存储类型的不同,数组有静态数组 ( static) 和动态数组 ( auto) 之分;根据定义的位置不同,数组有内部数组 ( 在函数内部定义的数组 ) 和外部数组 ( 在函数外部定义的数组 ) 之分 。
制 作:方 斌
[案例 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];
float ratio[4]={0.1,0.2,0.2,0.5}; /*定义成绩,比例系数数组 */
制 作:方 斌
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()函数等待从键盘上输入一个字符 */
}
}
制 作:方 斌
7.2.2维数组的定义和引用
7.2.1 2维数组的定义
7.2.2 2维数组元素的引用
7.2.3 2维数组元素的初始化
7.2.4 2维数组应用举例制 作:方 斌
7.2.1 二维数组的定义一、二维数组的定义一般形式:
类型说明符 数组名 [常量表达式 ][常量表达式 ]
例、
float a[3][4]; a为 3× 4(3行 4列 )的数组
float b[5][10]; b为 5× 10(5行 10列 )的数组二维数组的理解:
二维数组 a[3][4]理解为:有三个元素 a[0],a[1],a[2],
每一个元素是一个包含 4个元素的数组。
制 作:方 斌二维数组的元素在内存中的存放循序:
按行存放,即:先顺序存放第一行的元素,再存放第二行的元素。(最右边的下标变化最快,第一维的下标变化最慢)。
三维数组,float a[2][3][4]在内存中的存放顺序:
a[0][0][0]→ a[0][0][1]→ a[0][0][2]→ a[0][0][3]→
a[0][1][0]→ a[0][1][1]→ a[0][1][2]→ a[0][1][3]→
a[0][2][0]→ a[0][2][1]→ a[0][2][2]→ a[0][2][3]→
a[1][0][0]→ a[1][0][1]→ a[1][0][2]→ a[1][0][3]→
a[1][1][0]→ a[1][1][1]→ a[1][1][2]→ a[1][1][3]→
a[1][2][0]→ a[1][2][1]→ a[1][2][2]→ a[1][2][3]
制 作:方 斌二、二维数组中元素的引用用数组名和下标引用元素。例、
float a[2][3]; 有 6个元素,按如下方式引用各元素:
a[0][0],a[0][1],a[0][2],a[1][0],a[1][1],a[1][2]
注意:数组 float a[2][3]中无元素 a[2][3]。(下标从 0始)
三、二维数组的初始化
1、分行赋值,如、
static int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
2、全部数据写在一个大括号内,如、
static int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
3、部分元素赋值,如、
static int a[3][4] = {{1},{5},{9}};
仅对 a[0][0],a[1][0],a[2][0]赋值,其余元素未赋值(对于静态数组,
编译器自动为未赋值元素指定初值 0;对于动态数组,未赋值元素的初值是随机的)。
制 作:方 斌
4、如果对全部元素赋初值,则第一维的长度可以不指定,但必须指定第二维的长度。例、
static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
与下面定义等价:
static int a[ ][4]={1,2,3,4,5,6,7,8,9,10,11,12};
四、二维数组程序举例
[例 7.4] 将一个二维数组行和列交换,存到另一个二维数组中。例如、
教材 p127~ 128还列举了一些部分元素赋初值的例子,请自行阅读。
算法,b[j][i] = a[i][j]
制 作:方 斌程序:
main()
{
static int a[2][3] = {{1,2,3},{4,5,6}};
static int b[3][2],i,j;
printf("array a:\n");
for(i=0;i<=1;i++) /* 0~ 1行 */
{
for(j=0;j<=2;j++) /* 0~ 2列 */
{
printf("%5d",a[i][j]);
b[j][i] = a[i][j]; /* 行、列交换 */
}
printf("\n"); /*输出一行后换行 */
}
printf("array b:\n");
for(i=0;i<=2;i++)
{
for(j=0;j<=1;j++)
printf("%5d",b[i][j]);
printf("\n"); /*输出一行后换行 */
}
}
制 作:方 斌
[例 7.5] 有一个 3× 4的矩阵,要求编程序以求出其中值最大的那个元素的值及其所在的行号和列号。
算法:首先把第一个元素 a[0][0]作为临时最大值 max,然后把临时最大值 max与每一个元素 a[i][j]进行比较,若 a[i][j]>max,把 a[i][j]作为新的临时最大值,并记录下其下标 i和 j。当全部元素比较完后,max是整个矩阵全部元素的最大值。
流程:
制 作:方 斌程序:
main()
{
int i,j,row=0,colum=0,max;
static int a[3][4]={{1,2,3,4},{9,8,7,6},{-10,10,-5,2}};
max = a[0][0];
for(i=0; i<=2; i++) /* 用两重循环遍历全部元素 */
for(j=0; j<=3; j++)
if (a[i][j] > max )
{ max = a[i][j];
row = i;
colum = j;
}
printf("max=%d,row=%d,colum=%d\n",max,row,colum);
}
注意:本例中得到的行列值从 0始。
制 作:方 斌[案例 1] 给一个 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");
for(i=0;i<Row;i++) /*输出 2维数组 array*/
{ for(j=0;j<Col;j++)
printf("%d\t",array[i][j]); /*将 a[i][j]的值显示在屏幕上 */
printf("\n");
}
getch();
}
制 作:方 斌复习:
二维数组的定义方式如下:
数据类型 数组名 [行常量表达式 ][列常量表达式 ][,数组名
2[行常量表达式 2][列常量表达式 2]……];
1.数组元素在内存中的排列顺序为“按行存放”,即先顺序存放第一行的元素,再存放第二行,以此类推。
2,设有一个 m*n的数组 x,则第 i行第 j列的元素 x[i][j]在数组中的位置为,i*n+j( 注意,行号、列号均从 0开始计数)。
3,可以把二维数组看作是一种特殊的一维数组:它的元素又是一个 1维数组 。
例如,对 x[3][2],可以把 x看作是一个一维数组,它有 3个元素:
x[0],x[1],x[2],每个元素又是一个包含 2个元素的 1维数组,即把
x[0],x[1],x[2]看作是 3个一维数组的名字 。
制 作:方 斌引用二维数组元素的形式为:
数组名 [行下标表达式 ][列下标表达式 ]
1.,行下标表达式,和,列下标表达式,,都应是整型表达式或符号常量 。
2.,行下标表达式,和,列下标表达式,的值,都应在已定义数组大小的范围内 。 假设有数组 x[3][4],则可用的行下标范围为
0~2,列下标范围为 0~3。
3,对基本数据类型的变量所能进行的操作,也都适合于相同数据类型的 2维数组元素 。
制 作:方 斌二维数组元素的初始化
1,按行赋初值数据类型 数组名 [行常量表达式 ][列常量表达式 ]= {{第 0行初值表 },{第 1行初值表 },……,{最后 1行初值表 }};
赋值规则:将,第 0行初值表,中的数据,依次赋给第 0行中各元素;将,第 1行初值表,中的数据,依次赋给第 1行各元素;以此类推 。
2,按二维数组在内存中的排列顺序给各元素赋初值数据类型 数组名 [行常量表达式 ][列常量表达式 ]= {初值表 };
赋值规则:按 2维数组在内存中的排列顺序,将初值表中的数据,
依次赋给各元素 。
如果对全部元素都赋初值,则“行数”可以省略。 注意,只能省略“行数”。
制 作:方 斌二维数组应用 案 例
[案例 2] 有 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,0},
{88,91,89,93,0},{72,65,54,75,0},
{86,88,75,60,0},{69,60,50,72,0},
{0,0,0,0,0}};
制 作:方 斌
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课程平均 ");
/*输出每门课程的平均成绩 */
printf(“\t”);
for(j=0;j<NUM_course;j++)
printf(“%6.1f\t",score[NUM_std][j]);
printf("\n");
getch();
}
制 作:方 斌
78 85 83 65 0
88 91 89 93 0
72 65 54 75 0
86 88 75 60 0
69 60 50 72 0
0 0 0 0 0
初始状态,0 1 2 3 4
0
1
2
3
4
5
for(i=0; i<NUM_std; i++) /* NUM_stu=5 */
{for(j=0; j<NUM_course; j++) /* NUM_course=4 */
{
}
}
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门课的平均成绩 */
311
361
266
309
251
393 389 351 365
77.8
90.3
66.5
77.3
62.8
78.6 77.8 70.2 73.0
制 作:方 斌输出结果,
学生编号 课程 1 课程 2 课程 3 课程 4 个人平均学生 1 78 85 83 65 77.8
学生 2 88 91 89 93 90.3
学生 3 72 65 54 75 66.5
学生 4 86 88 75 60 77.3
学生 5 69 60 50 72 62.8
----------------------------------------------------
课程平均
78.6 77.8 70.2 73.0
制 作:方 斌
7.3 字符数组与字符串
7.3.1 字符数组的定义
7.3.2 字符数组的初始化
7.3.3 字符数组的引用
7.3.4 字符串和字符串结束标志
7.3.5 字符数组的输入和输出
7.3.6 常用的字符串处理函数制 作:方 斌字符数组:存放字符数据的数组,每一个元素存放一个字符。
一、程序中定义字符数组例、
char c[10]; /* 定义 c为字符数组,包含 10个元素 */
c[0]='I'; c[1]=' '; c[2]='a'; c[3]='m'; c[4]=' ';
c[5]='h'; c[6]='a'; c[7]='p'; c[8]='p'; c[9]='y';
注意:
字符型与整型可以通用,但有区别:
char c[10]; /* 在内存中占 10字节 */
int c[10]; /* 在内存中占 20字节 */
制 作:方 斌二、字符数组的初始化
1、逐个元素初始化
static char c[10] = {'I',' ','a','m',' ','h','a','p','p','y'};
2、初始化数据少于数组长度,多余元素自动为“空”( '\0',二进制 0)。
static char c[10] = {'c',' ','p','r','o','g','r','a','m'}; /* 9 */
3、指定初值时,若未指定数组长度,则长度等于初值个数。
static char c[ ] = {'I',' ','a','m',' ','h','a','p','p','y'};
三、字符数组的引用引用一个元素,得到一个字符。
制 作:方 斌
[例 7.6] 输出一个字符串。
main()
{static char c[10]={'I',' ','a','m','
','a',' ','b','o','y'};
int i;
for(i=0;i<10;i++)
printf("%c",c[i]);
printf("\n");
}
输出结果:
I am a boy
制 作:方 斌
[例 7.7] 输出一个钻石图形。
main()
{static char diamond[][5]={{' ',' ','*'},
{' ','*',' ','*'},
{'*',' ',' ',' ','*'},
{' ','*',' ','*'},
{' ',' ','*'} };
int i,j;
for(i=0;i<5;i++)
{ for(j=0;j<5;j++)
printf("%c",diamond[i][j]);
printf("\n");
}
}
输出形状:
制 作:方 斌四、字符串字符串例子,"I am a boy"
1,C语言中,字符串作为字符数组处理。字符数组可以用字符串来初始化,例、
static char c[] = {"I am happy"};
也可以这样初始化:(不要大括号)
static char c[] = "I am happy";
2、字符串在存储时,系统自动在其后加上结束标志 '\0'(占一字节,其值为二进制 0)。但字符数组并不要求其最后一个元素是 '\0',例、
static char c[] = {"China"};
static char c[5] = {"China"};
static char c[10] = {"China"};
制 作:方 斌五、字符数组的输入输出两种方法:
1、用,%c”格式符逐个输入输出。
2、用,%s”格式符按字符串输入输出。例、
static char c[6];
scanf("%s",c);
printf("%s",c);
注意:
( 1)输出时,遇 '\0'结束,且输出字符中不包含 '\0'。
( 2),%s”格式输出字符串时,printf()函数的输出项是字符数组名,
而不是元素名。
static char c[6] = "China";
printf("%s",c); printf("%c",c[0]);
printf("%s",c[0]); ×
制 作:方 斌
( 3),%s”格式输出时,即使数组长度大于字符串长度,
遇 '\0'也结束。例、
static char c[10] = {"China"};
printf("%s",c); /*只输出 5个字符 */
( 4),%s”格式输出时,若数组中包含一个以上 '\0',
遇第一个 '\0'时结束。
( 5)输入时,遇回车键结束,但获得的字符中不包含回车键本身( 0x0D,0x0A),而是在字符串末尾添 '\0'。
因此,定义的字符数组必须有足够的长度,以容纳所输入的字符。(如,输入 5个字符,定义的字符数组至少应有 6个元素)。
制 作:方 斌
( 6)一个 scanf函数输入多个字符串,输入时以“空格”键作为字符串间的分隔。例、
static char str1[5],str2[5],str3[5];
scanf("%s%s%s",str1,str2,str3);
输入数据,How are you?
str1,str2,str3获得的数据见右图。
例、
static char str[13];
scanf("%s",str);
输入,How are you?
结果:仅,How”被输入数组 str
如要想 str获得全部输入(包含空格及其以后的字符),程序应设计为:
static char c[13];
int i;
for(i=0;i<13;i++) c[i] = getchar();
制 作:方 斌
( 7) C语言中,数组名代表该数组的起始地址,因此,scanf()函数中不需要地址运算符 &。
例、
static char str[13];
scanf("%s",str);
scanf("%s",&str);
C语言中,数组名代表该数组的起始地址。如右图所示的数组 C,占 6个字节。
在程序中只写数组名 C,它就代表地址
2000。如
Printf(“%o”,c);
输出结果:
2000 /*数组 C在内存中的首地址 */ \0
a
n
i
h
C
2005
2004
2003
2002
2001
2000
C语言实际上是按字符数组名找到数组的起始地址,然后逐个输出一个个字符,直到遇到字符结束符 \0为止。其他类型的数组名其实也是该数组在内存中的起始地址 (基地址 )
制 作:方 斌
[案例 7.5]从键盘输入一个字符串,回车键结束,并将字符串在屏幕上输出 。
/*案例代码文件名,AL6_5.C*/
main()
{int i;
static char str[80];
clrscr();
for(i=0;i<80;i++)
{ str[i]=getchar(); /*逐次给数组元素 str[i]赋值,但不回显在屏幕上 */
printf("*"); /*以星号代替输入字符的个数 */
if(str[i]=='\x0d') break; /*若输入回车则终止循环 */
}
i=0;
while(str[i]!='\x0d')
printf("%c",str[i++]); /*逐次输出字符数组的各个元素 */
printf("\n");
getchar(); /*程序暂停 */
}
制 作:方 斌复习:
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”格式符 。 另外,从键盘上输入字符时,无需输入字符的定界符 ── 单引号;输出时,系统也不输出字符的定界符 。
制 作:方 斌7.3.2 字符数组的整体操作
[案例 7.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]代表该行数组元素的首地址 */
getchar();
}
1.字符串及其结束标志所谓字符串,是指若干有效字符的序列。 C语言中的字符串,可以包括字母、
数字、专用字符、转义字符等。
C语言规定:以‘ \0?作为字符串结束标志(‘ \0?代表 ASCII码为 0的字符,表示一个“空操作”,只起一个标志作用)。因此可以对字符数组采用另一种方式进行操作了 ── 字符数组的整体操作 。
制 作:方 斌注意,由于系统在存储字符串常量时,会在串尾自动加上 1个结束标志,所以无需人为地再加 1个 。
另外,由于结束标志也要在字符数组中占用一个元素的存储空间,
因此在说明字符数组长度时,至少为字符串所需长度加 1。
2,字符数组的整体初始化字符串设置了结束标志以后,对字符数组的初始化,就可以用字符串常量来初始化字符数组 。
3,字符数组的整体引用
( 1) 字符串的输入除了可以通过初始化使字符数组各元素得到初值外,也可以使用
scanf()函数输入字符串 。
( 2) 字符串的输出
printf()函数,不仅可以逐个输出字符数组元素,还可以整体输出存放在字符数组中的字符串 。
制 作:方 斌
7.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() 函数来处理 。
制 作:方 斌
[案例 7.7] gets函数和 strcmp函数的应用 。
/*案例代码文件名,AL6_7.C,功能:简单密码检测程序 */
#include "stdio.h"
main()
{char pass_str[80]; /*定义字符数组 pass_str*/
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) 函数功能:将字符串中小写字母转换成大写,其它字符
( 包括大写字母和非字母字符 ) 不转换 。
制 作:方 斌
[例 7.9] 输入三个字符串,并找出其中最大者。
分析:用 strcmp()函数比较字符串的大小。首先比较前两个,把较大者拷贝给字符数组变量 string(用 strcpy()函数拷贝),再比较 string和第三个字符串。
程序:设字符串最长为 19个字符。
#include "string.h" /* strcmp,strcpy函数均在 string.h中定义 */
main()
{
char string[20]; /* 存最大字符串 */
char str[3][20]; /* 三个字符串 */
int i;
for(i=0;i<3;i++) gets(str[i]); /* 输入三个字符串 */
if (strcmp(str[0],str[1]) > 0) strcpy(string,str[0]);
else strcpy(string,str[1]);
if (strcmp(str[2],string) > 0) strcpy(string,str[2]);
printf("\nthe largest string is,\n%s\n",string);
}
制 作:方 斌本章要求及作业数组是程序设计的常用数据结构,字符串(字符数组)在现代程序中也用得相当普遍,应掌握它们的意义和用法。
1、数组(一维和二维,下同)名、数组元素的概念。
2、数组的初始化方法。
3、数组元素在内存中的存储顺序。
4、数据排序算法。
5、字符串的特点(作为数组处理、最后一字节加 '\0')。
6、字符串处理函数的应用。
作业:
7.2,7.3,7.4,7.6,7.7,7.9,7.10,7.13
上机:
( 1)作业及熟悉 Turbo C在线帮助系统的用法。