第七章 数 组
本章重点:
处理好循环与数组下标的关系,就掌握了数组的核心技术.
数组的赋值方法.
一些典型算法:排序,查找,插入的过程字符数组和字符串的根本区别在于程序在处理字符串时是以\0作为结束标志的.
字符串不能用赋值语句.
7.1一维数组的定义和引用
1.一维数组的定义
定义方式为,
类型说明符 数组名[常量表达式]
例如:int a[10];

数组名后面的常量表达式只能用“[ ]”括起来,不能用“( )”号。
2,一维数组元素的引用
每次可引用一个数组元素,不能引用整个数组。
一个数组元素就如同一个简单变量.
引用时应注意下标的值不要超过数组长度的范围。
a[0]=a[5]+a[7]-a[2*3] 常量和表达式做下标
a[i]=a[i]+1; 变量做下标
a[a[0]]=>a[1] 数组元素做下标
例7.1 程序自已输入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]);
}
3.一维数组的初始化
(1)在定义数组时对各元素指定初始值。
int a[10]={1,2,3,4,5,6,7,8,9,10};
等价于
int a[10];
a[0]=1;a[1]=2;a[2]=3; … a[9]=10;
可只对一部分元素赋以初值。
int a[5]={1,3,5};
a[0]=1;a[1]=3;a[2]=5; a[3],a[4]未确定
(3)数组可以不规定维数,在初始化赋值时,数组维数从0 开始被连续赋值。
例如,
char f[]={'a','b','c'};
int a[ ]={1,3,5};
(4)数组不赋初值,系统自动给所有实数组元素赋以0值。数组元素的赋值可以通过赋值语句或scanf函数输入。
例7.2:求一组数(10个)的平均与总和。E5_1a
main()
{ int i,sum,a[10];
float av;
for(i=0;i<10;i++)
scanf("%d",&a[i]); /*数据输入*/
sum=0;
for(i=0;i<10;i++) {
printf("%d ",a[i]); /*数据输出。并求和*/
sum=sum+a[i];
}
av=sum/10.0;
printf("%d\n",sum);
printf("%f\n",av);
}
4.一维数组举例例7.3:对10个数由小到大排序(冒泡排序)。
冒泡排序法的基本思想:
将相邻两个数相比较,将小的调到前头。不断比较,直到将最后两数比较处理完毕。每进行一轮,把剩下的数中最大的一个移到最后位置。

#include,stdio.h”
main()
{ int a[10]={9,8,5,4,2,0,6,1,3,7}
int i,n=10;
for (i=0;i<n-1;i++)
for (j=0;j<n-i;j++)
if (a[j]>a[j+1])
{ temp=a[j],a[j]=a[j+1];a[j+1]=temp;}
for (i=0;i<9;i++)
printf(“%3d”,a[i]);
}
例7.4:对N个数由小到大排序(选择排序)。
选择法的基本思想:
从所有元素中选择一个最小元素放在a[0],作为第一轮,第二轮从a[1]开始到最后的各元素中选择一个最小元素,放在a[1]中,依次类推,n个数进行n-1轮。
3 5 1 4 7 2
第一轮:1 8 5 7 3 10 
第二轮:1 3 5 7 8 10 
for(i=0,i<10,i++)
{ k=i;
for(j=i+1,j<10,j++)
if ( a[j]<a[k]) k=j //k记住最小值的下标
if (i!=k) { temp=a[i];a[i]=a[k];a[k]=temp;}
}
例7.5 折半查找法。
顺序查找及折半查找。
折半查找方法的介绍前提:查找数据已按一定规律(升序或降序)排列好。
基本思想:先检索正中间一个数据,看它是否为所需的数据,若不是,则判断数据在正中间数的哪一边,若在左侧,就在左侧范围查找,否则,在右侧查找。
查找过程:
 例:有一组已排序的数据:
8 13 21 28 35 41 52 63 71 76 81 95 101 150 164 
按树形排列如下:

Main()
{ int a[15]={8,13,21,28,35,41,52,63,71,76,81,95,101,150,164};
int m=15,n=0,y,x;
printf("enter x:");
scanf("%d",&x);
do
{ y=(m+n)/2;
if ((x>a[y] && x<a[y+1]) || (x<a[y] && x>a[y-1]))
{printf("no find!");break;}
if (x<a[y]) m=y;
if (x>a[y]) n=y;
if (x==a[y]) printf("%d at the position %d",x,y+1);break;}
} while (1);
}
7.2 二维数组
7.2.1.二维数组
(1)一般说明格式是,
类型 数组名[第1维长度][第2维长度]
(2).例如,
int m[3][2]; /*定义一个整数型的二维数组*/
char c[3][4]; /*定义一个字符型的三维数组*/
数组m[3][2]共有3*2=6个元素,顺序为,
m[0][0],m[0][1],m[1][0],m[1][1],m[2][0],m[2][1];
数组c[3][4]共有3*4=12个元素,顺序为,
c[0][0] c[0][1],c[0][2],c[0][3]
c[1][0] c[1][1],c[1][2],c[1][3]
c[2][0] c[2][1],c[2][2],c[2][3]
(3),数组占用的内存空间(即字节数)的计算式为,
字节数=第1维长度*第2维长度*该数组数据类型占用的字节数
7.2.2.二维数组的初始化
( 1)
int p[2][3]={{2,-9,0},{8,2,-5}}; /*定义数组p并初始化/*
int m[2][4]={{27,-5,19,3},{1,8,-14,-2}};
(2) 多维数组存储是连续的,因此可以用一维数组初始化的办法来初始化多维数组。
例如,
int x[2][3]={1,2,3,4,5,6}; /*用一维数组来初始化二维数组*/
(3) 对数组初始化时,如果初值表中的数据个数比数组元素少,则不足的数组元素自动用0来填补。
3,二维数组的引用
行号和列号都是从0开始的,并注意行号和列号不要超过数组定义的范围。
a[0][0]=1; a[0][1]=2;….
main()
{
int m[2][2];
float n[3];
m[0][0]=0,m[0][1]=17,m[1][0]=21;/*数组元素赋值*/
n[0]=109.5,n[1]=-8.29,n[2]=0.7;
,
,
,
}
例7.6二维数组元素的输入。
例7.7 将一个2x3矩阵转置。

分析:对数组a,列先与行变化,对于数组b,行先于列变化.
for (i=0;i<2;i++)
for (j=0;j<3;j++)
b[j][i]=a[i][j]
4.多维数组
多维数组的一般说明格式是,
类型 数组名[第n维长度][第n-1维长度]......[第1维长度];
char c[2][2][3]; /*定义一个字符型的三维数组*/
数组c[2][2][3]共有2*2*3=12个元素,顺序为,
c[0][0][0],c[0][0][1],c[0][0][2],
c[0][1][0],c[0][1][1],c[0][1][2],
c[1][0][0],c[1][0][1],c[1][0][2],
c[1][1][0],c[1][1][1],c[1][1][2],
7.3 字符数组和字符串
问题:
(1)什么是字符串?字符串与字符常量有何区别?
1.字符数组
(1) 字符数组的定义
char s[10];
(2)字符数组的初始化
①,逐个为数组各元素指定一个初值字符
char c[10]={‘c’,’ ‘,’h’,’a’ }
注意:初始化字符个数大于字符数组长度,则出现语法错误。
如果初始个数小于数组长度,则其余的元素自动定为空字符(即‘\0’).

② 对一个字符数组指定一个字符串初值
char c[]={”I am a student”}或char c[]=”I am a student”
些时数组的长度应为字符串个数加1。(为什么?)
字符数组的输入输出。
①单个字符数组元素的输入输出用scanf,printf函数的“%c”格式。
②将整个字符串一次输入或输出,用scanf,printf函数的“%s”格式
char st[10];
scanf(“%s”,str); /*用数组名代表数组的起始地址,因此不需要地址符号。*/
printf(“%s”,str);
③输入一个字符串时,从键盘直接输入,然后按回车。不需要输入双引号。若输入的字符串中有空格,只接收空格前的字符。输入的字符串中有空格,可用gets函数输入。
④若数组长度大于字符串实际长度,也是输出到遇‘\0’结束。
⑤若字符数组中包含多个’\0’,则遇第一个’\0’时输出结束。
⑥输出字符不包括结束符‘\0’.
例:输入输出字符串。
输入 windows & c↙的输出结果如何?
3.字符数组与字符串
(1) 字符数组不一定有结束标志‘\0’;有结束标志的字符数组是字符串。
(2) 字符串可以存放在字符数组中,但字符串与字符数组可以长度不相等。
char c[]={“char”};
等价于 static char c1[5]={‘c’,’h’,’a’,’r’,’\0’};
不等价于 static char c2[4]={‘c’,’h’,’a’,’r’};
7.4 字符串的处理函数
1.puts()和gets()函数
(1),puts()函数
puts()函数用来向标准输出设备(屏幕)写字符串并换行,其调用格式为,
puts(s);
其中s为字符串变量(字符串数组名)。
puts()函数的作用与语printf("%s\n",s)相同。
例,
main()
{
char s[20]= "Thank you"; /*定义字符串数组和指针变量*/
puts(s);
}
说明,
① puts()函数只能输出字符串,不能输出数值或进行格式变换。
② 可以将字符串直接写入puts()函数中。如,
puts("Hello,Turbo C2.0");
(2),gets()函数
gets()函数用来从标准输入设备(键盘)读取字符串直到回车结束,但回车符不属于这个字符串。其调用格式为,
gets(s);
其中s为字符串变量(字符串数组名)。
例:7.8
main()
{
char s[20];
printf("What's your name?\n");
gets(s); /*等待输入字符串直到回车结束*/
puts(s); /*将输入的字符串输出*/
puts("How old are you?");
puts(f);
}
2、字符串运算函数
字符串运算函数在头文件string.h中,因些程序中要用字符串运算函数,必须用 #include,string.h”。
(1)字符串拷贝函数
①strcpy(字符数组1,字符串2)
②作用:将字符串2拷贝到字符数组1中。
Char str1[10],str2[]={“china”};
Strcpy(str1,str2); /* 问str1=”china”; str1=str2对不对?);
③ strcpy(str1,str2,2)的作用。/*拷贝前2个字符*/
不能用赋值语句将一个字符串常量或字符数组直接赋给一个字符数组。但可以给一个字符变量或字符数组元素赋值。
如下面的赋值是不合法的。
static char str1={“China”},str2[20];
str2=str1;(╳)
下面的赋值是合法的。
char a[5],c1,c2;
c1=‘A’; c2=‘B’;
a[0]=‘C’;a[1]=‘h’,a[2]=‘i’;a[3]=’n’;a[4]=’a’
(2)字符串连接函数
strcat(字符数组1,字符数组2)
作用:将两个字符串连接成为一个字符串。
(3)字符串比较函数
strcmp(str1,str2)
功能:当str1与str2完全相同,返回值为0。
 当str1>str2,则返回值为正整数
 当str1<str2,则返回值为负整数比较规则:一个一个比较,直到出现不同的字符或遇到“\0”为止。
注意:两个字符串不能用关系运算符比较,如下面的写法是不合法的:
if (str1==str2) printf(”yes”);
应写成 if (strcmp(str1,str2)==0)
printf(“yes”);
 (4)求字符串长度函数
strlen(“computer”)
注意:函数返回值不包括’\0’在内,即测出字符串的实际字符个数。
例7.9:输入一行字符,统计出有多少个单词。单词字间用空隔分隔。
#include "stdio.h"
main()
{
char string[81];
int i,num=0,word=0;
char c;
gets(string);
for(i=0;(c=string[i])!='\0';i++)
if (c==' ') word=0;
else if (word==0)
{ word=1;num++;}
printf("there are %d words in the line\n",num);
}
小经验:
当处理的数据很多,又希望用循环来控制时,使用数组当运算的中间结果需要保存时,使用数组一维数组典型用法排序查找插入删除分类统计