数组
引言:至今我们已经学过了单项数据的变量。现在, 我们将会看到包含多项数据的变量。在C 语言中, 这种变量被称作数组。数组很方便地把一系列相同类型的数据保存在一起。数组还被用来构造许多其它的数据结构: 树、表等等。数据结构一般大家如果想学,推荐清华大学的蔚严敏《数据结构》,我现在看的就是我大二师姐的数,感觉还不错。
数组在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组。在C语言中, 数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。我们这里介绍数值数组和字符数组,其余的在以后各章陆续介绍。
数组类型说明:在C语言中使用数组必须先进行类型说明。 数组说明的一般形式为: 类型说明符 数组名 [常量表达式],……; 其中,类型说明符是任一种基本数据类型或构造数据类型。 数组名是用户定义的数组标识符。 方括号中的常量表达式表示数据元素的个数,也称为数组的长度。
例如:
int a[1]; 说明整型数组a,有1个元素。
float b[1],c[2]; 说明实型数组b,有1个元素,实型数组c,有2个元素。
对于数组类型说明应注意以下几点:
1.数组的类型实际上是指数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相同的。
2.数组名的书写规则应符合标识符的书写规定。
3.数组名不能与其它变量名相同.如:
void main()
{
int a;
float a[1];
}
这样就错了!
4.方括号中的常量表达式表示了数组元素的个数,如a[2]表示数组a有2个元素。但是其下标从0开始计算。因此5个元素分别为a[0],a[1]。
5.不能在方括号中用变量来表示元素的个数, 但是可以是符号常数或常量表达式。例如:
void main()
{
int a=1
int b[1+2],c[1+a];
……
}
是合法的。但是下述说明方式是错误的。
void main()
{
int n=5;
int a[n];
……
}
6.允许在同一个类型说明中,说明多个数组和多个变量。
例如: int a,b,c,d,k1[10],k2[20];
一维数组
一维数组的定义::   数据类型  数组名[常量表达式];  
数组其实和变量一样必须先定义,后使用,只能逐个引用数组元素,不能一次引用整个数组;数组元素表示形式:  数组名[下标]
其中:下标可以是常量或整型表达式,:例  
     int a[10];
           printf(“%d”,a);         (这个就错了哦)
必须  int a[10];
           printf(“%d\\t”,a[j]);               

一维数组的初始化:在定义数组时,为数组元素赋初值(在编译阶段使之得到初值)如:
int a[5]={1,2,3,4,5};
等价于:a[0]=1;  a[1]=2; a[2]=3; a[3]=4; a[4]=5;
数组初始化说明:
<1>数组不初始化,其元素值为随机数
<2>对static数组元素不赋初值,系统会自动赋以0值。如:
static int a[5];等价于:a[0]=0;  a[1]=0; a[2]=0; a[3]=0; a[4]=0;
<3>只给部分数组元素赋初值。如:
int a[5]={6,2,3};等价于:a[0]=6; a[1]=2;a[2]=3; a[3]=0; a[4]=0;
<4>当全部数组元素赋初值时,可不指定数组长度
int a[]={1,2,3,4,5,6};编译系统根据初值个数确定数组维数
我们还是用例子说话,现在来看看几个关于一维数组的程序设计经典题目
例题1:用数组来处理Fibonacci问题。
Fibonacci这个问题一直是C语言的经典题型,这个问题是这样的:有一对兔子,从出生以后第三个月起每个月都生一对兔子(似乎兔子繁殖能力太强了哈),小兔子长到三个月后每个月又生了一对兔子,现在假设所有的兔子都不死,问每个月兔子的总数为多少?只求前20个月.
分析,其实仔细分析下题目,就会发现每个月的兔子的总数是:1,1,2,3,5,8,13……一下子就转换为一个数列问题:
我们先不用数组写出程序:
#include<stdio.h>
void main()
{
long int a,b;/*因为数值很大,所以要用长整型*/
int c;
a=1;
b=1;
for(c=1;c<=20;c++)
{
printf(“%12ld %12ld”,a,b);/*因为a,b用的十长整型,所以printf函数的输出格式用“%12ld”*/
if(c%2==0)
printf(“\\n”);
a=a+b;
b=b+a;
}
}
好了,我们现在用数组做下:
#include <stdio.h>
main()
{   int i;
     int f[20]={1,1};
    for(i=2;i<20;i++)
       f=f[i-2]+f[i-1];
    for(i=0;i<20;i++)
    {   if(i%5==0)  printf(“\\n”);
         printf(“%12d”,f);
    }
}
是不是更加的简洁了呢?具因为这两段代码来至书上,请大家自己看书!

例题2:现在再来看看对输入的10个数进行的排序,
代码1:我 加了注释就比较便于理解了哈
void main()
{
int a[10],b,max;
printf(“input 10 numbers:\\n”);
for(b=0;b<10;b++)  /**逐个输入10个数到数组a中/
scanf(“%d”,&a);
max=a[0];          /*a[0]送入max中*/
for(b=1;b<10;b++)  /*从a[1]到a[9]逐个与max中的内容比较*/
if(a>max) max=a;/*若比max的值大,则把该下标变量送入max中*/
printf(“maxnum=%d\\n”,max);
}
第二段代码用的是冒泡发对数进行排序
我们先来回顾下冒泡法的算法说明:
排序过程:
(1)比较第一个数与第二个数,若为逆序a[0]>a[1],则交换;然后比较第二个数与第三个数;依次类推,直至第n-1个数和第n个数比较为止——第一趟冒泡排序,结果最大数 被安置在最后一个元素位置上
(2)对前n-1个数进行第二趟冒泡排序,结果使次大的数被安置在第n-1个元素位置
(3)重复上述过程,共经过n-1趟冒泡排序后,排序结束
代码2:
#include <stdio.h>
main()
{   int a[11],i,j,t;
    printf(“Input 10 numbers:\\n”);
    for(i=1;i<11;i++)
       scanf(“%d”,&a);
    printf(“\\n”);
    for(j=1;j<=9;j++)
       for(i=1;i<=10-j;i++)
          if(a>a[i+1])
         {
         t=a; a=a[i+1]; a[i+1]=t;
         }
    printf(“The sorted numbers:\\n”);
    for(i=1;i<11;i++)
printf(“%d “,a);
}

我们再用选择法对它进行排序
排序过程:
(1)首先通过n-1次比较,从n个数中找出最小的, 将它与第一个数交换—第一趟选择排序,结果最小的数被安置在第一个元素位置上
(2)再通过n-2次比较,从剩余的n-1个数中找出关键字次小的记录,将它与第二个数交换—第二趟选择排序
(3)重复上述过程,共经过n-1趟排序后,排序结束
代码如下:
void main()
{
int i,j,p,q,s,a[10];
printf(“\\n input 10 numbers:\\n”);
for(i=0;i<10;i++)
scanf(“%d”,&a);
for(i=0;i<10;i++)
{
p=i;q=a;
for(j=i+1;j<10;j++)
if(q<a[j]) { p=j;q=a[j]; }
if(i!=p)
{
s=a;
a=a[p];
a[p]=s;
}
printf(“%d”,a);
}
}
相信现在大家对一维数组理解比较透彻了!

二维数组
前面介绍的数组只有一个下标,称为一维数组, 其数组元素也称为单下标变量。在实际问题中有很多量是二维的或**的, 因此C语言允许构造**数组。**数组元素有多个下标, 以标识它在数组中的位置,所以也称为多下标变量。 本小节只介绍二维数组,**数组可由二维数组类推而得到。二维数组类型说明二维数组类型说明的一般形式:
类型说明符 数组名[常量表达式1][常量表达式2]…;
其中常量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度。如:
int a[3][4]; 说明了一个三行四列的数组,数组名为a,其下标变量的类型为整型。该数组的下标变量共有3×4个。
二维数组在概念上是二维的,即是说其下标在两个方向上变化, 下标变量在数组中的位置也处于一个平面之中, 而不是象一维数组只是一个向量。但是,实际的硬件存储器却是连续编址的, 也就是说存储器单元是按一维线性排列的。 如何在一维存储器中存放二维数组,可有两种方式:一种是按行排列, 即放完一行之后顺次放入第二行。另一种是按列排列, 即放完一列之后再顺次放入第二列。在C语言中,二维数组是按行排列的。所以我们可以把它当成矩阵来理解,这样就比较好接受了.
二维数组的初始化和一维基本类似,只需要注意:如果要对全部元素赋初值,则第一维的长度可以不给出。
例如: static int a[3][3]={1,2,3,4,5,6,7,8,9}; 可以写为:static int a[][3]={1,2,3,4,5,6,7,8,9};
我们还是来看例子吧:
例题1:一个学校的某年级有3个班,每个班有三门课的考试成绩。求全年级分科的平均成绩和各科总平均成绩。
班级     Math  C  English
 1      70  69    71
  2      61  63   79       
  3      60  68   60
  
void main()
{
int i,j,s=0,l,v[3],a[5][3];
printf(“input score\\n”);
for(i=0;i<3;i++){
for(j=0;j<3;j++)
{ scanf(“%d”,&a[j]);
s=s+a[j];}
v=s/3;
s=0;
}
l=(v[0]+v[1]+v[2])/3;
printf(“math:%d\\nc languag:%d\\English:%d\\n”,v[0],v[1],v[2]);
printf(“total:%d\\n”,l);
}
代码分析:程序中首先用了一个双重循环。 在内循环中依次读入成绩,并把这些成绩累加起来,退出内循环后再把该累加成绩除以3送入v之中,这就是该门课程的平均成绩。外循环共循环三次,分别求出三门课各自的平均成绩并存放在v数组之中。退出外循环之后,把v[0],v[1],v[2]相加除以3即得到各科总平均成绩。最后按题意输出各个成绩。
我们再来看一个题目,
例题2:求n*n的矩阵转置:
分析:这个题目是书上求3*4矩阵的转置自的变形,初看这个题目无从下手,但是我们学习了数组以后就会方便很多了,知识需要把数组之间调换就可以了,看我写的代码:
#define  ROW 4
main
{
int sm[ROW],i,j,temp;
printf(“Input elements of a matrix(%d*%d)\\n.ROW,ROW”);
for(i=0;i<ROW; i++)
  for(j=o,j<ROW;j++)
    scanf(“%d”,&sm[]i[j]);
  for(i=0;i<ROW-1:i++)/*开始转置了*/
   for(j=i+1;j<ROW;j++)
   {
     temp=sm[j];
     s[j]=s[j];
     s[j]=temp;
   }
printf(“that is:\\n”);
for(i=0;j<ROW;I++)
  {
    printf(“%5d,sm[j]”);
    printf(“\\n”)
  }
}  
大家看懂没有呢?还可以回顾一下for循环的嵌套哦,对于初学者这段程序可能觉得头大,那我们现在就来分析一下:在这个程序中利用了矩阵的上三角和下三角的对换
循环控制就是:
for(i=0;i<ROW-1:i++)/*开始转置了*/
for(j=i+1;j<ROW;j++);
这样就使得j总是大于i了,也就是列大于行然后又开始转换 :
temp=sm[j];
     s[j]=s[j];
     s[j]=temp;
这个也就是 s[j]和s[j]互换,用temp来做中间变量,这个就好像是两杯水要互相换下,就必须要在拿第三个杯子,这样以后上三角内容和下三角互换,完成了转置,
但是要是大家把循环写成:
for(i=0;i<ROW:i++)
for(j=0;j<ROW;j++);
这样上三角和下三角就会互换两次,最后的结果也就是没有达到转置的效果;

字符数组
  用来存放字符量的数组称为字符数组。 字符数组类型说明的形式与前面介绍的数值数组相同。例如: char c[10]; 由于字符型和整型通用,也可以定义为int c[10]但这时每个数组元素占2个字节的内存单元。字符数组也可以是二维或**数组,例如: char c[5][10];即为二维字符数组。 字符数组也允许在类型说明时作初始化赋值。例如: static char a[10]={`c`,` `,`p`,`r`,o`,g`,r`,`a`,`m`};赋值后各元素的值为: 数组a a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]其中c[9]未赋值,由系统自动赋予0值。 当对全体元素赋初值时也可以省去长度说明。
还是来看代码
main()
{
int i,j;
char a[][4]={{\’B\’,\’A\’,\’S\’,\’I\’,\’C\’,},{\’l\’,\’o\’,\’v\’,\’e\’}};
for(i=0;i<=1;i++)
{
  for(j=0;j<=4;j++)
  printf(“%c”,a[j]);
  printf(“\\n”);
}
}
本例的二维字符数组由于在初始化时全部元素都赋以初值, 因此一维下标的长度可以不加以说明。字符串在C语言中没有专门的字符串变量, 通常用一个字符数组来存放一个字符串。在前面介绍字符串常量时,已说明字符串总是以\’\\0\’作为串的结束符。因此当把一个字符串存入一个数组时, 也把结束符\’\\0\’存入数组,并以此作为该字符串是否结束的标志。 有了\’\\0\’标志后,就不必再用字符数组的长度来判断字符串的长度了。

字符串常用函数
 C语言提供了丰富的字符串处理函数, 大致可分为字符串的输入、输出、合并、修改、比较、转换、复制、搜索几类。 使用这些函数可大大减轻编程的负担。用于输入输出的字符串函数, 在使用前应包含头文件”stdio.h” ; 使用其它字符串函数则应包含头文件”string.h”。 下面介绍几个最常用的字符串函数。其他的以后在作介绍:
1.puts函数:字符串输出函数 puts
格式: puts (字符数组名) 功能:把字符数组中的字符串输出到显示器。 即在屏幕上显示该字符串
main()
{
static char c[]=”BASIC\\love”;
puts(c);
}
分析:从程序中可以看出puts函数中可以使用转义字符, 因此输出结果成为两行。puts函数完全可以由printf函数取代。 当需要按一定格式输出时,通常使用printf函数。
2.gets函数:字符串输入函数gets
格式: gets (字符数组名) 功能:从标准输入设备键盘上输入一个字符串。 本函数得到一个函数值,即为该字符数组的首地址。
main()
{
char st[10];
printf(“input string:\\n”);
gets(st);
puts(st);
}
分析:可以看出当输入的字符串中含有空格时,输出仍为全部字符串。说明gets函数并不以空格作为字符串输入结束的标志, 而只以回车作为输入结束。这是与scanf函数不同的。
3.strcat函数:字符串连接函数
格式: strcat (字符数组名1,字符数组名2) 功能:把字符数组2中的字符串连接到字符数组1 中字符串的后面,并删去字符串1后的串标志“\\0”。本函数返回值是字符数组1的首地址。
main()
{
static char st1[10]=”My love is “;
int st2[10];
printf(“input your name:\\n”);
gets(st2);
strcat(st1,st2);
puts(st1);
}
分析:程序把初始化赋值的字符数组与动态赋值的字符串连接起来。 要注意的是,字符数组1应定义足够的长度,否则不能全部装入被连接的字符串
4.strcpy函数:字符串拷贝函数
格式: strcpy (字符数组名1,字符数组名2) 功能:把字符数组2中的字符串拷贝到字符数组1中。串结束标志“\\0”也一同拷贝。字符数名2, 也可以是一个字符串常量。这时相当于把一个字符串赋予一个字符数组。
main()
{
static char st1[20],st2[]=”C Language”;
strcpy(st1,st2);
puts(st1);printf(“\\n”);
}
分析:函数要求字符数组1应有足够的长度,否则不能全部装入所拷贝的字符串。
5.strcmp函数:字符串比较函数
格式: strcmp(字符数组名1,字符数组名2) 功能:按照ASCII码顺序比较两个数组中的字符串,并由函数返回值返回比较结果。
字符串1=字符串2,返回值=0;
字符串2〉字符串2,返回值〉0;
字符串1〈字符串2,返回值〈0。
函数也可用于比较两个字符串常量,或比较数组和字符串常量。
main()
{ int a;
static char st1[20],st2[]=”C Language”;
printf(“input a string:\\n”);
gets(st1);
k=strcmp(st1,st2);
if(a==0) printf(“st1=st2\\n”);
if(a>0) printf(“st1>st2\\n”);
if(a<0) printf(“st1<st2\\n”);
}
分析:程序中把输入的字符串和数组st2中的串比较,比较结果返回到k中,根据k值再输出结果提示串。当输入为dbase时,由ASCII 码可知“dBASE”大于“C Language”故k〉0,输出结果“st1>st2”。
6.strlen函数:测字符串长度函数strlen
格式: strlen(字符数组名) 功能:测字符串的实际长度(不含字符串结束标志‘\\0’) 并作为函数返回值。
main()
{ int k;
static char st[]=”C language”;
k=strlen(st);
printf(“The lenth of the string is %d\\n”,k);
}
7. strlwr函数:转换字符串大小写
格式:strlwr(字符串),功能转换字符串大小写
8.strupr函数:转换字符串大写
格式:strupr(字符串),功能转换字符串大小写

我们现在还是来看具体的例题哈
例题1:把5个人的名字按照字母顺序输出。
分析:本题编程思路如下:3个人名应由一个二维字符数组来处理。然而C语言规定可以把一个二维数组当成多个一维数组处理。 一个一维数组就是一个人名字符串。用字符串比较函数比较各一维数组的大小,并排序,再输出,代码如下:
void main()
{
char st[20],cs[3][20];
int i,j,p;
printf(“input people\’s name:\\n”);
for(i=0;i<3;i++)
gets(cs);/*输入三个人名字符串*/
printf(“\\n”);
for(i=0;i<3;i++)
{
  p=i;strcpy(st,cs);
  for(j=i+1;j<3;j++)
  if(strcmp(cs[j],st)<0) {p=j;strcpy(st,cs[j]);}
  if(p!=i)
   {
    strcpy(st,cs);
    strcpy(cs,cs[p]);
    strcpy(cs[p],st);
   }
  puts(cs);
}
printf(“\\n”);
}
程序分析:本程序的第一个for语句中,用gets函数输入五个人名字符串。上面说过C语言允许把一个二维数组按多个一维数组处理, 本程序说明cs[5][20]为二维字符数组,可分为五个一维数组cs[0],cs[1],cs[2],cs[3],cs[4]。因此在gets函数中使用cs是合法的。 在第二个for语句中又嵌套了一个for语句组成双重循环。 这个双重循环完成按字母顺序排序的工作。在外层循环中把字符数组cs中的人名字符串拷贝到数组st中,并把下标i赋予P。 进入内层循环后,把st与cs以后的各字符串作比较,若有比st小者则把该字符串拷贝到st中,并把其下标赋予p。内循环完成后如p不等于 i 说明有比cs更小的字符串出现,因此交换cs和st的内容。 至此已确定了数组cs的第i号元素的排序值。然后输出该字符串。在外循环全部完成之后即完成全部排序和输出。

评论被关闭。