欢迎光临
免费的PDF电子书下载网站

C语言程序设计(第4版) PDF下载

编辑推荐

《C语言程序设计(第4版)》的习题经过精心设计,第5~9章设计了一道系列习题,循序渐进,*终完成题目,使读者得到系统性训练。《C语言程序设计(第4版)》*后一章为综合程序设计,详细介绍程序设计的总体方法与过程。《C语言程序设计(第4版)》除了PPT课件和全部例子、习题的源码,还配套有实验教材《C语言程序设计(第4版)实验指导与习题解答》(9787302495932),详细解析课后实验题目。 ;

内容简介

本书是面向程序设计初学者的C语言基础教材,以培养大学生的逻辑思维能力和程序设计能力为编写指导思想,综合运用案例教学、比较教学、任务驱动等多种教学方法,系统介绍C语言程序设计的基本理论、基本方法和基本过程。本书内容组织注重基础,突出应用,兼顾提高,强化主干知识,弱化细枝末节; 实例设置注重易学性、趣味性和系列化,易教易学。 全书共10章,内容包括程序设计概述、简单程序设计、选择结构程序设计、循环结构程序设计、数组程序设计、函数程序设计、指针程序设计、结构体程序设计、文件程序设计以及综合程序设计。 本书配有教学课件、例题及习题程序源代码等教学资源,并有辅导教材《C语言程序设计(第4版)实验指导与习题解答》(ISBN9787302495932)。 本书适合作为高等院校“C语言程序设计”课程的教材,也可用作程序设计从业人员及程序设计爱好者的自学参考书。

作者简介

暂无

C语言程序设计(第4版) PDF下载

目录

 ;目录

第1章程序设计概述

1.1程序设计语言

1.2算法

1.2.1算法概念与算法描述

1.2.2算法的逻辑结构

1.2.3算法的特性

1.2.4算法评价

1.3程序设计与实现

1.3.1程序设计的基本过程

1.3.2编辑运行C语言程序

1.4C语言程序的基本结构

1.4.1程序的函数化结构

1.4.2标识符与保留字

1.4.3程序风格

小结

习题一

第2章简单程序设计

2.1数据类型、常量与变量

2.1.1数据类型

2.1.2常量

2.1.3变量

2.2数据的输入与输出

2.2.1用printf()函数输出数据

2.2.2用scanf()函数输入数据

2.2.3字符的输入与输出

2.3简单运算

2.3.1算术运算

2.3.2赋值运算

2.3.3变量自增和自减运算

2.3.4逗号运算

2.4编译预处理命令简介

2.5简单程序设计举例

2.6表达式中数据类型的转换

*2.7定义数据类型别名

*2.8const常量

小结

习题二

第3章选择结构程序设计

3.1if选择结构

3.1.1if选择结构程序示例

3.1.2关系表达式

3.1.3逻辑表达式

3.1.4if命令

3.1.5条件运算

3.2switch选择结构

3.3选择结构程序举例

小结

习题三

第4章循环结构程序设计

4.1循环结构控制命令

4.1.1while命令

4.1.2dowhile命令

4.1.3for命令

4.2循环体中的控制命令

4.2.1break命令

4.2.2continue命令

4.3循环嵌套

4.4goto命令

4.5循环结构程序举例

小结

习题四

第5章数组程序设计

5.1一维数组程序设计

5.1.1一维数组程序示例

5.1.2一维数组的定义及元素引用

5.1.3数值型一维数组的输入和输出

5.1.4数值型一维数组的初始化

5.1.5字符型一维数组的初始化

5.1.6一维数组的存储

5.2字符串操作

5.2.1字符串的输入和输出

5.2.2多字符串操作函数

5.3二维数组程序设计

5.3.1二维数组的定义及元素引用

5.3.2二维数组的输入和输出

5.3.3二维数组的初始化

5.3.4二维数组的存储

5.4数组应用程序举例

小结

习题五

第6章函数程序设计

6.1函数概述

6.2函数定义及调用

6.2.1函数定义

6.2.2函数值和return命令

6.2.3函数调用

6.3函数嵌套和递归函数

6.3.1函数嵌套

6.3.2递归函数

6.4数组与函数

6.4.1数组元素作函数参数

6.4.2一维数组名作函数参数

6.4.3二维数组与函数

6.5函数应用程序举例

6.6变量的作用域和存储类型

6.6.1变量的作用域

6.6.2变量的存储类型

*6.7编译连接多个源文件的C程序

小结

习题六

第7章指针程序设计

7.1指针概述

7.2指针变量的定义和使用

7.2.1指针变量程序示例

7.2.2定义指针变量

7.2.3使用指针变量

7.3指针与数组

7.3.1指针与一维数组

7.3.2指针与二维数组

7.3.3指针与字符串

7.3.4指针数组

7.4指针作函数参数

7.4.1简单变量指针作函数参数

7.4.2指向数组的指针作函数参数

7.4.3字符串指针作函数参数

7.4.4指针数组作函数参数

*7.4.5使用带参数的main()函数

7.5指针函数

7.6指针应用程序举例

小结

习题七

第8章结构体程序设计

8.1结构体数据概述

8.2结构体类型和结构体变量

8.2.1结构体程序示例

8.2.2定义结构体数据类型

8.2.3结构体变量的定义及使用

8.3结构体数组

8.3.1结构体数组的定义及元素引用

8.3.2结构体数组的初始化

8.3.3结构体数组应用实例

8.4结构体指针变量

8.4.1结构体指针变量的定义及使用

8.4.2结构体指针作函数的参数

8.5使用链表存储数据

8.5.1使用链表存储数据示例

8.5.2链表的特点

8.5.3动态内存管理函数

8.5.4定义链表结构

8.6链表的基本操作

8.6.1链表结点的插入

8.6.2链表结点的删除

8.6.3链表结点的查找

8.7结构体应用程序举例

8.8动态数组

小结

习题八

第9章文件程序设计

9.1文件概述

9.1.1文件的概念

9.1.2文件的分类

9.1.3文件的一般操作过程

9.1.4文件类型指针

9.2文件的基本操作

9.2.1打开和关闭文件

9.2.2文件的字符读写

9.2.3文件结束状态测试

9.2.4文件的数据块读写

9.3文件的其他操作

9.3.1文件位置指针的定位

9.3.2文件的格式化读写

9.3.3文件的字符串读写

9.4文件应用程序举例

小结

习题九

第10章综合程序设计

10.1软件开发流程

10.2通讯录程序设计

10.2.1通讯录程序需求分析

10.2.2通讯录程序功能设计

10.2.3通讯录程序数据设计

10.2.4通讯录程序函数设计

10.2.5函数编码及测试

附录AC语言经典保留字

附录B常用C语言库函数

附录C字符与ASCII码对照表

附录DC语言的运算符

附录E“学生数据处理”系列例题(习题)简表

参考文献

前沿

前言
承蒙广大师生的厚爱和清华大学出版社的支持,近几年来在清华大学出版社出版了多种版本的C语言程序设计教材,实验指导与习题解答的内容以简化版形式附在主教材中合并出版。本次应广大师生要求编写了辅导教材《C语言程序设计(第4版)实验指导与习题解答》(ISBN9787302495932),并借此机会对《C语言程序设计(第3版)》进行了改版,编写了本书。与前一版本相比,本书主要进行了以下改进。第一,改正了原有教材中存在的错误和不当之处,力求概念准确,表达恰当。第二,更新例题,优化代码。一是剔除了不易讲解的例题,更新了不够经典的老例题,充实了系列例题,更易于讲解; 二是注重基础例题题目与提高题目相结合,适合不同层次、不同兴趣的学生学习; 三是对原有部分例题的程序代码进行了优化,更简明易读。第三,增加综合程序设计,强化应用能力培养。本书增加了“第10章综合程序设计”,该章以通讯录程序设计为实例,体现软件工程思想,针对C语言结构化程序设计的特点,详细介绍了C语言应用程序的设计方法与过程。第四,以新颖性、趣味性和系列化为重点对课后习题进行了优化。一是对编程题目进行了较大幅度的更新,提高题目的趣味性和吸引力; 二是提高练习题目的系列化程度,便于知识的连贯性学习和系统训练。例如,在第5~9章增加了Josephus环报数游戏程序设计系列习题,并作为实验必做题目列入了实验指导内容。本书是面向程序设计初学者的C语言基础教材,突出C语言程序设计的应用性、实践性特点,突出主干知识教学,注重逻辑思维能力和基本程序设计能力的培养,适合程序设计初学者学习使用。本书以实例引领教学内容,符合认知规律。凡是适合以程序实例开始的新知识均通过程序实例和程序说明予以引导,首先建立感性认识,然后进行相关知识的系统介绍。本书注重理论实践相结合,讲解重点突出。通过大量设计性实例培养学生的程序设计能力,按照问题分析与算法设计、程序实现、程序说明及进一步讨论等内容进行系统讲解,注重算法设计、关键语句、关键程序段以及程序讨论的分析说明,重点、难点讲解透彻,而且富有启发性。全书共10章,内容包括程序设计概述、简单程序设计、选择结构程序设计、循环结构程序设计、数组程序设计、函数程序设计、指针程序设计、结构体程序设计、文件程序设计以及综合程序设计。本书有配套的教学课件、例题程序源代码、习题程序源代码以及《C语言程序设计(第4版)实验指导与习题解答》等教学资源。冯伟昌、王宗江、黄忠义、刘海慧、张莹、李竹健、张元国、王桂东、魏建国、王金才、张文、高永存、王涛、薛莹、徐英娟、马明祥、滕秀荣、张敏、魏军、徐兴敏、周金玲、
彭玉忠、潘振昌、徐思杰等参与了本书的编写并做了大量素材整理、程序调试、书稿审校等工作,在此表示感谢!清华大学出版社付弘宇编辑和她的同事们为本书的编辑、出版做了大量严谨细致的工作,在此一并致谢!
编者2018年3月

免费在线读

第5章数组程序设计

数组是C语言的一种重要数据结构,使用数组可以实现一组同类型数据的连续存储和有效处理。本章介绍使用数组的程序设计,包括一维数组和二维数组的定义、初始化、在计算机中的存储及其使用方法,字符串的输入与输出操作及常用的字符串操作函数,并通过大量实例介绍数组应用程序的设计方法。5.1一维数组程序设计本节首先通过批量处理数据的一个简单示例说明数组在数据处理中的作用,然后逐步介绍一维数组程序设计的基本知识。5.1.1一维数组程序示例下面是一个简单的数据处理程序,其功能是从键盘输入10个整数,然后按照与输入相反的顺序依次将它们输出。
/*program e5-0.c*/
#include


int main()

{  ;  ; ;

int a,b,c,d,e,f,g,h,i,j; ;

printf("Input Data:"); ;

scanf("%d%d%d%d%d%d%d%d%d%d",&;a,&;b,&;c,&;d,&;e,&;f,&;g,&;h,&;i,&;j);

printf("Output Data: ");

printf("%d %d %d %d %d %d %d %d %d %d\n",j,i,h,g,f,e,d,c,b,a);

return 0;

}

这是一个正确的程序,但并不是一个好程序。如果处理的数据规模进一步扩大,有成千上万的数据时,变量的这种表示方法显然是不适用的,很难想象以类似的方式定义和使用成千上万的变量时程序会是什么样子。本章要讨论的数组将有效解决上述问题。数组是包含多项同类数据的一种数据结构,它能将一系列相同类型的数据组织起来,使用同一个名称,再用下标进行分量标识。例如a[0]、a[1]、…、a[9]等,当下标用一个变量i表示时i的不同取值即对应不同的分量,使用a[i]即可访问这一组数据的任何一个分量,这里的a就是一个数组。以下是用数组解决上述问题的程序。【例51】从键盘输入10个整数,然后按照与输入相反的顺序依次将它们输出。程序如下: ;

#include


int main()

{

int i,a[10];  ;  ;  ;  ;  ;   /*定义a数组*/

printf("Input:  ;");

for(i=0;i<;10;i ) ;

scanf("%d",&;a[i]);   /*输入a[0]、a[1]、a[2]、…、a[9]等数据*/

printf("Output: ");

for(i=9;i>;=0;i--)

printf("%d ",a[i]);  ;  /*输出a[9]、a[8]、a[7]、…、a[0]等数据*/

return 0;

}

程序执行结果: ;

Input:0 1 2 3 4 5 6 7 8 9 ;

Output: 9 8 7 6 5 4 3 2 1 0

该程序中定义了一维数组a,用a[i]表示数组a的一个元素,当i为0时即为a[0],当i为5时即为a[5]。“scanf("%d",&;a[i]);”语句执行10次,i依次取值0到9,故依次为a[0]到a[9]输入数据。“printf("%d",a[i]);”语句也执行10次,i依次取值9到0,故依次输出a[9]到a[0]的值。5.1.2一维数组的定义及元素引用1.  ;一维数组的定义一维数组在使用之前必须先定义,一般格式如下: ;

数据类型数组名[数组长度]

例如: ;

int a[10];

该语句定义了数组名为a的int型数组,该数组有10个元素,能够存储10个整数值。

char name[20];

该语句定义了数组名为name的char型数组,该数组有20个元素,能够存储20个字符。说明: ;(1) 数组的数据类型即数组元素的数据类型,它可以是C语言允许的任何数据类型。(2) 数组长度是数组能够包含的数组元素的个数,通常用一个整数值表示,也可以是常量表达式,但它不允许是包括变量的表达式。例如,下面对数组的定义方法是错误的,其原因是定义数组长度时使用了变量n。

int n=10;

float a[n];

2.  ;一维数组的元素引用一维数组的输入、输出、运算等一般通过数组元素进行。数组元素的引用形式如下: ;

数组名[下标]

数组元素的下标从0开始,当数组长度为n时最末元素的下标是n-1。例如: ;上述a数组的各元素依次为a[0]、a[1]、…、a[8]、a[9]。上述name数组的各元素依次为name[0]、name[1]、…、name[18]、name[19]。在实际使用中,下标可以是一个整数型常量,也可以是整数型表达式,最为常用的是用一个整数型变量表示元素的下标。例如上述a数组,其任意元素可表示为a[i],当指定i的值后a[i]则表示a数组的一个特定元素。例如当i=0时,a[i]即代表a[0]元素,当i=1时a[i]即代表a[1]元素,以此类推。5.1.3数值型一维数组的输入和输出数值型数组的输入和输出是通过每一个数组元素的输入和输出实现的。数值型一维数组的元素都是一些简单变量,输入和输出按照简单变量的方法进行。例如对于上述a数组,输入a[5]的值时使用如下语句: ;

scanf("%d",&;a[5]);

输出a[5]的值时使用如下语句: ;

printf("%d",a[5]);

【例52】向数组输入10个整数,通过相邻元素比较、交换的方法,将最大值移到数组末尾,然后输出该数组中所有元素的值。1) 算法设计设用数组a存储数据,它的10个元素分别为a[0]、a[1]、a[2]、…、a[8]和a[9],这10个元素比较9次即可将最大值移到最后,即a[9]元素位置。每次比较时,进行比较的两个相邻元素分别为a[i]和a[i 1],比较示意图如图51所示,算法流程图如图52所示。

图51比较示意图

图52例52算法流程图

2) 实现程序程序如下: ;

#include


#define N 10

int main()

{

int a[N],i,temp;  ;
  /*定义整数型数组a*/

for(i=0;i

  /*为数组a输入数据*/

scanf("%d",&;a[i]);  ;  ;  ;  ; ;

for(i=0;i


  /*把数组a中的最大数后移*/

if(a[i]>;a[i 1])

{  ;  ;  ;  ;   /*相邻元素前者大、后者小时交换两个元素的值*/

temp=a[i];

a[i]=a[i 1];

a[i 1]=temp;

}

for(i=0;i


  /*输出数组a的所有元素值*/

printf("%d ",a[i]);

return 0;

}

下面是程序的执行结果: ;

输入数据: 19 7 21 6 25 8 17 20 11 10 ;

输出结果: 7 19 6 21 8 17 20 11 10 25

程序中使用了3个并列的for循环。第1个for循环用于输入10个整数,其中的a[i]是数组a的元素的一般表示,&;a[i]是它的地址形式。第2个for循环对数组a的10个元素从头开始进行两两比较,使大值后移,循环结束时数组a的最大值存储在最后一个元素中。N个元素共需比较N-1次。第3个for循环用于输出数组a的全部元素。● 问题思考该题目的数组a经过这样一趟共N-1次比较、交换后最大值移到了数组的最后位置。如果对其余的元素也同样进行一趟比较、交换会出现什么结果?5.1.4数值型一维数组的初始化数组的初始化是指在定义数组时对数组元素赋初值。通常有两种情况,即全部元素的初始化和部分元素的初始化。1.  ;全部元素的初始化其一般格式如下: ;

数据类型数组名[数组长度]={数组全部元素值表}

“数组全部元素值表”是用逗号分隔的各数组元素的初值。例如: ;

int a[6]={10,20,30,40,50,60};

该语句定义了整数型数组a,它有6个元素a[0]~a[5],初值依次为10、20、30、40、50、60。当需要对全部元素初始化时数组长度允许省略,格式如下: ;

数据类型数组名[]={数组全部元素值表}

当用这种省略方式初始化数组时,数组的长度由“数组全部元素值表”中值的个数确定。例如: 

float r[]={12.5,-3.11,8.6};

该语句定义了长度为3的float型数组r,其数组元素r[0]、r[1]、r[2]的初值分别为12.5、-3.11、8.6。2.  部分元素的初始化其使用较多的情况是对前部元素的初始化。一般格式如下: 

数据类型数组名[数组长度]={数组前部元素值表}

例如: 

int b[10]={1,2,3};

该语句定义了整数型数组b,它有10个元素,前3个元素b[0]、b[1]、b[2]的初值分别为1、2、3,其余元素的初值由数组a的存储属性决定。变量的存储属性将在后续内容中介绍。用户必须注意,当只对数组的部分元素初始化时数组长度的说明是不能省略的。另外,由于数组元素本身是一个变量,因此可以使用赋值语句对其单独赋值。例如: 

int array[10];

array[5]=26;

array[7]=38;

图53Fibonacci数列算法流程图

【例53】将Fibonacci数列的前20项存储在一维数组中,然后输出这些数据。1) 算法设计在第4章已经讨论过Fibonacci数列问题。读者将会发现,用数组处理这个问题也是一种有效的方法。首先定义一个int型一维数组fib并初始化: 

int fib[20]={1,1};

初始化后,元素fib[0]和fib[1]的值即是Fibonacci数列前两项的值。那么,Fibonacci数列自第3项起每一项可按以下方式求值,并存储在fib数组中。

fib[i]=fib[i-1] fib[i-2] (i=2,3,4,…,19)

求解Fibonacci数列的算法流程图如图53所示。2) 实现程序程序如下: 

#include


int main()

{

int fib[20]={1,1},i;    
  /*fib数组初始化*/

for(i=2;i<20;i )     

fib[i]=fib[i-1] fib[i-2];
  /*第i项为其紧邻的前两项之和*/

for(i=0;i<20;i )      
  /*控制输出每一个数值*/

{

printf("%-10d",fib[i]);
  /*输出1个Fibonacci数值*/

if((i 1)%5==0)    
  /*每输出 5个数之后换行*/

printf("\n");      

}

return 0;

}

程序执行结果: 

11235

813213455

89144233377610

9871587258441816765

程序执行后,Fibonacci数列的前20个数在fib数组中的存储情况如图54所示。

图54存储在fib数组中的Fibonacci数列

将Fibonacci数列前两项的值存储在fib[0]、fib[1]中的操作也可由以下赋值语句实现。

fib[0]=fib[1]=1;

● 问题思考在该程序中,生成Fibonacci数列和输出Fibonacci数列是分开进行的,能否修改程序将这两个过程合并在一个步骤中?5.1.5字符型一维数组的初始化字符型数组是数据类型为char型的数组,用于存储字符串,每一个元素存储一个字符。字符型数组与数值型数组在本质上没有区别,但在具体使用时还是有其自身的特点。(1) 使用字符常量对字符数组初始化。例如: 

char string[8]={e,x,a,m,p,l,e,\0};

上面的语句定义了字符数组string,该数组共有8个元素,string[0]到string[6]这7个元素的初始值分别为字符常量e、x、a、m、p、l、e,string[7]的初始值为转义字符常量\0。\0是C语言字符串的结束标志,在进行字符串处理时它标志一个字符串的结束。(2) 使用字符串对字符数组初始化。例如: 

char string[8]="example";

当使用这种方式对字符数组初始化时,系统自动在字符串尾部增加一个结束标志\0,使元素string[7]自动获得\0结束符,各元素的初始化情况与(1)相同。(3) 在初始化时可省略数组长度说明,数组的实际长度由系统根据初始化的形式确定。例如: 

char string[]="example";

对于这种情况,系统将根据存储长度自动设置数组string的长度为8。5.1.6一维数组的存储任何一个一维数组在内存中都占用一段连续的存储空间,依次存储它的各元素的值。上述数组a及数组string的存储情况如图55所示,各个元素占用的字节数由数组的数据类型决定。数组a的每个元素为int型,VC 6.0编译系统为其分配4个字节的存储空间; 数组string的每个元素为char型,分配1个字节的存储空间。

图55一维数组的存储

5.2字符串操作字符串在数据处理中有重要应用,例如统计一段文字中单词的数量、按学生姓名查找学生信息等都是典型的字符串处理问题。C语言使用字符数组存储字符串,并且为方便字符串处理,专门设置了功能丰富的字符串操作函数。5.2.1字符串的输入和输出C语言提供了多个函数支持字符串的输入和输出操作,例如专门的字符串输入输出函数gets()和puts()、格式化输入输出函数scanf()和printf()等。1.  使用gets()函数和puts()函数输入、输出字符串实现字符串的输入和输出操作最常用的方法是使用gets()函数和puts()函数,这两个函数专门为字符串的输入和输出设计。1) 使用gets()函数输入字符串gets()函数的功能是从标准输入设备输入一个字符串,并存储在指定数组中。其一般用法如下: 

gets(字符数组名)

例如: 

char str[12];

gets(str);

执行gets()函数后,从键盘输入一个字符串存储到str数组中。gets()函数以Enter键作为输入结束符,而在字符数组中对应存储一个字符串结束标记\0。2) 使用puts()函数输出字符串puts()函数的功能是输出存储在字符数组中的字符串。其一般用法如下: 

puts(字符数组名)

例如: 

char c[6]="China";

puts(c);

该puts()函数被执行后,立即输出存储在字符数组C中的字符串。结果如下:

China

【例54】用gets()函数输入一个字符串,将其存储到str数组中,然后使用puts()函数输出str中的字符串。程序如下: 

#include


#define N 100

int main()

{

char str[N];            /*定义字符数组str,用于存储字符串*/

printf("String: ");

gets(str);              /*输入字符串,存储到str数组中*/

printf("Result: ");

puts(str);             /*输出str数组中的字符串*/

return 0;

}

程序执行结果: 

String:This is a example. 

Result: This is a example.

2.  使用scanf()和printf()输入、输出字符串在格式化输入输出函数scanf()和printf()中设置了%s格式符,专门用于字符串的输入和输出。【例55】用scanf()函数输入一个字符串,将其存储到str数组中,然后使用printf()函数输出str中的字符串。程序如下: 

#include


#define N 100

int main()

{

char str[N];              /*定义字符数组str,用于存储字符串*/

printf("String: ");

scanf("%s",str);          /*输入字符串,存储到str数组中*/

printf("Result: ");

printf("%s\n",str);    
  /*输出str数组中的字符串*/

return 0;

}

程序执行结果: 

String:example. 

Result: example.

再次执行: 

String:This is a example. 

Result: This

在使用scanf()函数和%s格式符输入字符串时需要注意以下几点: (1) 在C语言中,数组名代表数组的起始地址,因此使用字符数组接收字符串时在scanf()函数中直接使用该数组名。例如,上述程序中的“scanf("%s",str)”函数直接使用字符数组名str。(2) 在输入的字符串中,只有第1个空格(字符串前端空格除外)之前的字符串被读入到字符数组中。上面给出了程序两次执行的结果,在第2次执行程序时,输入字符串中有空格符,结果表明空格之后的字符串并未输入到字符数组str中。(3) 可以一次输入多个字符串,输入的各字符串之间要以“空格”分隔。例如: 

char str1[5],str2[5],str3[5];

scanf("%s%s%s",str1,str2,str3);

输入数据: How are you?  

则字符数组str1、str2、str3分别获得字符串"How"、"are"、"you?",其存储情况如图56所示。

图56数组str1、str2及str3的存储情况

● 问题思考(1) 在例54的程序中,字符串的输出操作是由“puts(str);”语句实现的,试将其改为由printf()函数实现,查看程序的执行结果有无变化。(2) 在例55的程序中,字符串的输出操作是由“printf("%s\n",str);”语句实现的,试将其改为由puts()函数实现,查看程序的执行结果有无变化。5.2.2多字符串操作函数多字符串操作函数具有较为复杂的函数原型,其函数类型、参数类型必须使用指针类型进行描述,相关知识迄今尚未介绍。本小节仅就已有知识简单介绍字符串操作函数的基本用法,函数原型请参阅附录B。1.  字符串连接函数strcat()使用格式: 

strcat(s1,s2)

函数功能: 将字符串s2连接到字符串s1的后面。说明: (1)  s1是字符数组名或字符数组的开始地址,s2既可以是字符数组名,也可以是字符串。(2) 函数在执行之后,s1是连接之后的字符串,s2保持不变。在定义s1数组时,其数组长度应不小于两个字符串的长度之和。【例56】将两个字符串连接为一个新字符串,并将该字符串输出。程序如下: 

#include


#include


int main()

{

char c1[20]="China",c2[10]= "man"; /*定义字符型数组并初始化*/

strcat(c1,c2);      /*将c2存储的字符串连接到c1存储的字符串之后*/

printf("String c1: ");

puts(c1);          /*输出字符串c1*/

printf("String c2: ");

puts(c2);          /*输出字符串c2*/

return 0;

}

执行结果: 

String c1: Chinaman

String c2: man

● 问题思考在上面的程序中,连接使用的两个字符串是在程序内部定义的,因此该程序并没有通用性。若要求通过键盘输入两个字符串,然后把它们连接起来,应该怎样修改程序?2.  字符串复制函数strcpy()使用格式: 

strcpy(s1,s2)

函数功能: 把字符串s2复制到字符数组s1中。说明: (1)  s1是字符数组名或字符数组的开始地址; s2可以是数组名或字符数组的开始地址,也可以是一个字符串。s1不能是字符串。(2)  s1数组的长度应不小于s2的长度,以保证能够存储s2,否则会出现意想不到的错误结果。【例57】字符串复制示例。程序如下: 

#include


#include


int main()

{

char c1[20]="program",c2[10]="example"; 

strcpy(c1,c2);/*把c2中的字符串复制到c1中,c1的原串被覆盖*/

printf("String c1: "); 

puts(c1);      /*输出c1中的字符串*/

printf("String c2: "); 

puts(c2);    /*输出c2中的字符串*/

return 0;

}

执行结果: 

String c1: example

String c2: example

3.  字符串比较函数strcmp()使用格式: strcmp(s1,s2)

函数功能: 比较字符串s1和字符串s2的大小。说明: (1) s1、s2可以是字符数组名或字符数组的开始地址,也可以是字符串。(2) 字符串比较就是比较字符串中字符的编码值(例如ASCII码值),编码值大的字符串大。比较的方法是对两个字符串自左至右逐个字符比较,直到遇到不同字符或字符串结束标记\0时比较过程结束,此时编码值大的字符所在的字符串大。(3)  strcmp()函数返回一个数值。当s1与s2相同时,strcmp(s1,s2)的值为0; 当s1大于s2时,strcmp(s1,s2)的值为一个正数; 当s1小于s2时,strcmp(s1,s2)的值为一个负数。注意: 字符串只能用strcmp()函数比较,不能用关系运算符“==”比较。例如,对于字符串s1、s2,若其相同时输出"yes",应使用如下语句: 

if(strcmp(s1,s2)==0) printf("yes");

下面的用法是错误的: 

if (s1== s1) printf("yes");

【例58】使用strcmp()函数设计一个密码验证程序。程序如下: 

#include


#include


#define N 3

int main()

{

int count=1;

char word[12];

while(count <=N)

{

printf("Pass word: ");

gets(word);             /*输入密码字*/

if(strcmp(word,"beijing2008")==0)

break;              /*比较成功则终止循环*/

}

if(count>N 1)               /*对结束循环的情况进行判断*/

printf("Sorry!\n");/*连续3次输入错误*/

else

printf("Continue,please!\n");/*输入了正确密码*/

return 0;

}

程序内设的密码字是字符串"beijing2008",当要求用户输入口令时,如果用户能正确地输入该字符串,则视为合法用户,可以继续运行程序。若连续3次都不能正确地输入该字符串,则视为非法用户,不能继续使用程序。● 问题思考(1) 在执行上述程序时,若密码字在第3次输入时才正确,结束while循环后count的值是多少?(2) 若连续3次都不能正确地输入密码字,结束while循环后count的值是多少?4.  其他字符串操作函数除上面介绍的字符串操作函数以外,字母的大小写转换函数、求字符串长度函数等也是常用的字符串操作函数,表51是关于这几个函数的基本描述。

表51其他几个常用的字符串操作函数

函数及用法函数功能说明

strlwr(s)将字符串s中的大写字母转换为小写字母strupr(s)将字符串s中的小写字母转换为大写字母strlen(s)求字符串s的长度s可以是字符数组名(字符串首地址),也可以是字符串常量

5.3二维数组程序设计数组分为一维数组、二维数组和多维数组,不同维数的数组既具有共同的性质,又具有各自不同的特点。本节对二维数组程序设计的基本知识进行介绍。5.3.1二维数组的定义及元素引用1.  二维数组的定义二维数组数据的排列通常具有如下形式: 263819552117661829651629横向的每一组数称为数组的一行,纵向的每一组数称为数组的一列。如果要定义二维数组,除了要说明它的元素的数据类型、数组名以外,还需说明数组的行数和列数。二维数组的一般定义格式如下: 

数据类型 数组名[表达式1][表达式2];

例如: 

int a[3][4];

该语句定义了数组名为a的int型二维数组,该数组有3行4列,共12个数组元素,每个数组元素均要用两个下标进行标识。如下: a[0][0]a[0][1]a[0][2]a[0][3]a[1][0]a[1][1]a[1][2]a[1][3]a[2][0]a[2][1]a[2][2]a[2][3]说明: (1) 二维数组的数据类型的说明与一维数组相同,都是指数组中每个元素的数据类型。(2)  “表达式1”用来定义二维数组的行数,“表达式2”用来定义二维数组的列数,“表达式1”和“表达式2”可以是整数型常量,也可以是由常量构成的整数型表达式,但不允许是变量表达式。例如,下面对数组的定义方法是错误的: 

int m=5,n=10;

float b[m][n];  

2.  二维数组元素的引用二维数组元素的引用形式如下: 

数组名[下标1][下标2]

其中,“下标1”和“下标2”允许是任何形式的整数型表达式,分别表示数组元素所在的行号和列号。C语言规定,二维数组的行下标和列下标都从0开始编号。对于上述a数组,行下标的取值范围为0~2,列下标的取值范围为0~3。通常使用a[i][j]表示二维数组a的任意元素,当指定i、j为特定值时a[i][j]则为数组的一个特定元素。例如,当i=0、j=0时a[i][j]即为a[0][0],当i=0、j=1时a[i][j]即为a[0][1],以此类推。用户也可以用一维数组的观点看待一个二维数组。对于M行N列的二维数组即可视为有M个元素的一维数组,其中每一个数组元素又是一个具有N个元素的一维数组。例如一个2行3列的二维数组a,可以视为包含a[0]、a[1]两个元素的一维数组,而a[0]、a[1]又是各包含3个元素的一维数组,其中a[0]的3个元素为a[0][0]、a[0][1]、a[0][2],a[1]的3个元素为a[1][0]、a[1][1]、a[1][2],此时可以将a[0]、a[1]视为数组名。5.3.2二维数组的输入和输出二维数组的输入和输出是通过每一个二维数组元素的输入和输出实现的。当数组元素是简单变量时,与简单变量的输入和输出方法相同。例如,对于上述a数组,输入a[1][2]的值时使用如下语句: 

scanf("%d",&a[1][2]);

输出a[1][2]的值时使用如下语句: 

printf("%d", a[1][2]);

对于M行N列的二维数组,如果要访问它的每一个元素,一般使用双重循环实现。【例59】有一个3行4列的二维数组,从键盘输入其前两行各元素的数据,并将这两行的数组元素按列求和的结果对应存储在第3行的各元素中。例如,设以下两行数据为该二维数组前两行的数据: 10203040

15253545按题目要求,首先将这两行数据对应输入到二维数组前两行的各元素中,然后按列求和,填充第3行的数据。以下是填充数据之后数组的最终结果: 10203040

15253545

25456585程序如下: 

#include


int main()

{

int a[3][4],i,j;

printf("Input data:\n");

for(i=0;i<2;i )          
  /*输入前两行的数据*/

for(j=0;j<4;j )

scanf("%d",&a[i][j]);

for(j=0;j<4;j )

a[2][j]=a[0][j] a[1][j];
  /*填充第3行的元素*/

printf("Result:\n");

for(i=0;i<3;i )              /*输出a数组的全部元素*/

{

for(j=0;j<4;j )

printf("%d   ",a[i][j]);

printf("\n");               /*每行输出结束后换行*/

}

return 0;

}

程序执行结果: 

Input data:

10 20 30 40 15 25 35 45 

Result:

10203040

15253545

25456585

该程序有两个双重循环,分别实现a数组的输入和输出。第1个双重循环的内循环体是“scanf("%d",&a[i][j]);”语句,该语句共执行8次,依次为数组元素a[0][0]、a[0][1]、a[0][2]、a[0][3]、a[1][0]、a[1][1]、a[1][2]、a[1][3]输入数据。在输入数据时需按照这一顺序对应提供数据,即按行逐列输入。数组a的输出是由后面一个双重循环实现的,输出结果分行显示。程序中间的一个for语句计算第3行每个元素的值,其循环体语句“a[2][j]=a[0][j] a[1][j];”共执行4次,依次为元素a[2][0]、a[2][1]、a[2][2]、a[2][3]赋值,即填充。5.3.3二维数组的初始化二维数组的初始化是在定义二维数组时为数组元素赋初值,既可对数组的全部元素初始化,也可对数组的部分元素初始化。1.  按行初始化按行初始化的思想是把二维数组的一行当作一个一维数组对待,每行提供一个独立的数据集合。例如: int a[2][3]={{1,2,3},{4,5,6}};

初值部分的{1,2,3}对应数组a[0]行的3个元素,{4,5,6}对应数组a[1]行的3个元素,每行元素的初始化方式与一维数组相同。初始化后a数组的各元素值如下: 

a[0][0]=1、a[0][1]=2、a[0][2]=3、a[1][0]=4、a[1][1]=5、a[1][2]=6

按行初始化时,也可以只对二维数组的部分元素初始化。例如: 

int a[2][3]={{1,2},{4}};

数组的a[0]行只有两个初值,按顺序分别赋给a[0][0]和a[0][1]; 数组的a[1]行只有一个初值4,赋给a[1][0]。2.  按行逐列初始化按行逐列初始化是二维数组常用的初始化方式,它把提供的初始化数据,按照逐行逐列的顺序依次赋给对应的数组元素。例如: 

int b[3][2]={10,20,30,40,50,60};

大括号“{ }”中的6个数据依次赋给b数组的元素b[0][0]、b[0][1]、b[1][0]、b[1][1]、b[2][0]、b[2][1]。按行逐列初始化也可以只对部分数组元素初始化。例如: 

int b[3][2]={ 10,20,30};

大括号“{}”内只有3个初值,对应赋给元素b[0][0]、b[0][1]、b[1][0]。3.  初始化时二维数组的行数定义部分允许省略例如: 

int a[][4]={{1,2},{1,2,3}};

int b[][3]={1,2,3,4,5,6,7,8,9};

数组a的初始化数据有两组,系统自动确定数组行数为2; 数组b的初始化数据共有9个,列数值为3,即每行3个数,所以数组b的行数是9/3=3,即数组b为3行3列。当数据总数不能被列数值整除时,数组行数值为商值加1。例如: 

int c[][3]={1,4,5,6,8};

在该数组定义中,所给出的初始化数值的个数为5,不能被数组的列数值3整除,系统按商值加1的原则,将数组c的行数定义为2,即数组c为2行3列的二维数组。读者务必注意,不管用哪一种方式对二维数组初始化,数组列数的定义都是不能省略的,即第2个维数不能省略。【例510】一个4×4数组a具有如下性质: 数组元素a[i][j]和a[j][i]对应相等。已知a的下三角区域的元素值如下,编写程序填充数组的其他元素。1

61

871

9531

程序如下: 

#include


int main()

{

int a[4][4]={{1},{6,1},{8,7,1},{9,5,3,1}}; /*部分元素初始化*/

int i,j;

for(i=0;i<3;i )

for(j=i 1;j<4;j )

a[i][j]=a[j][i];  /*利用下三角元素填充上三角元素*/

for(i=0;i<4;i )       /*输出填充后的二维数组a*/

{

for(j=0;j<4;j )

printf("%4d",a[i][j]);

printf("\n");

}

return 0;

}

程序执行结果: 

1689

6175

8713

9531

希望读者对照结果分析程序的执行过程。5.3.4二维数组的存储二维数组在计算机中存储时,计算机按照二维数组的大小分配一段连续的内存空间,逐个存储二维数组的各个元素,各个元素的存储采用按行逐列的顺序。例如,对于m×n的二维数组a,各元素的存储次序如下: 

a[0][0]、a[0][1]…a[0][n-1]、a[1][0]、a[1][1]…a[1][n-1]…a[m-1][0]、a[m-1][1]…a[m-1][n-1]

图57二维数组存储举例

系统为其分配的存储单元数为m×n×每个元素占用的存储单元数。其中,每个元素占用的存储单元数取决于数组的数据类型和使用的C语言系统。例如,2×2数组example的存储情况如图57所示。数组所占存储空间的首单元地址称为数组的首地址,该地址可直接使用数组名表示。数组名的这一性质在使用指针处理数组时被广泛应用。

5.4数组应用程序举例数组在批量数据处理中具有重要的应用,本节介绍的数组程序设计均是一维数组和二维数组应用的典型实例。【例511】对N个整数进行升序排序,并输出排序结果。1) 问题分析与算法设计排序是将一组数据按照一定的顺序排列起来,由小到大排列时称为升序排序,反之则为降序排序。在使用一维数组进行排序时,通常的过程是先将待排序的一组数据存储在数组中,然后使用一定的排序方法进行具体的排序操作。排序的方法有多种,这里使用冒泡排序算法。实际上,例52后移最大数的操作已经体现了冒泡排序的思想。冒泡排序的过程如下: 对于给定的待排序数据,从头开始依次对相邻的两个数据进行两两比较,当前者大时两数交换位置,直到比较完最后一个数据,此时这些数据的最大值处于最末位置,这称为一趟比较。然后对其余数据重复这种比较过程,直到排序结束。对于N个数据的数列需进行N-1趟排序操作。以下是对5个数据排序时每趟排序结束之后的情况,[]内的数据是按序定位的数据,不再参加下一趟的排序操作。待排序数列: 62821-195第1趟结束: 621-195[28]第2趟结束: 6-195[2128]第3趟结束: -195[62128]第4趟结束: [-19562128]从上述分析可知,冒泡排序需要双重循环实现: 外循环进行趟数控制,内循环将当前待排序数据中的最大数移到末端。若趟数控制变量用i表示,数组的任一元素用a[j]表示,在对5个数据进行排序时,每趟的待排序元素及其比较情况如图58所示,加阴影的元素是当前已经按序定位的元素,不再参加比较操作。

图58每趟待排序元素及其比较操作示意图

对照图58不难看出,若待排序元素数为N,则趟数控制变量i的取值范围为1~N-1。进行第i趟排序时,元素下标变量j的取值范围为0~(N-i)-1。

2) 实现程序程序如下: 

#include


#define N 10

int main()

{

int a[N],i,j,temp;

printf("Input data: ");

for(i=0;i


scanf("%d",&a[i]);

for(i=1;i


for(j=0;j


if(a[j]>a[j 1])  /*相邻元素前者大、后者小时两元素值交换*/

{

temp=a[j];

a[j]=a[j 1];

a[j 1]=temp;

}

printf("Result: ");

for(i=0;i


printf("%d  ",a[i]);

printf("\n");

return 0;

}

程序中第2、第3个for语句是一个双重循环结构,用于实现数组元素的排序。其中的外循环进行趟数控制,内循环控制每一趟参加比较的数据区间,并在该区间内自始至终对相邻的元素进行两两比较,使大数后移。每次内循环结束后,该数据区间中的最大值被移至该区间的最末元素位置。例如进行第3趟比较时,参加比较的数据区间为a[0]至a[2],该趟比较结束时,该区间中的最大值被移至a[2]元素位置,即a[2]为该区间的最大值。程序执行结果: 

Input data: 10 20 19 21 9 6 15 8 12 16 (输入数据)

Result: 68910121516192021(排序后的数列)

● 拓展知识类似上述程序,在调试时经常需要输入批量数据,低效而烦琐。此时可以利用C语言提供的随机数生成函数rand()自动生成一批数据,用于程序调试。rand()函数的说明在头文件stdlib.h中,使用时要在程序开头添加宏命令#include

。下面是对10个随机数排序的完整程序。

#include


#include


#define N 10

int main()

{

int a[N],i,j,temp;

printf("Data:  ");

for(i=0;i


{

a[i]=rand()%100;

printf("%4d",a[i]);   /*输出随机数*/ 

}

for(i=1;i

for(j=0;j

if(a[j]>a[j 1])

{

temp=a[j];

a[j]=a[j 1];

a[j 1]=temp;

}

printf("\nResult:");

for(i=0;i


printf("%4d",a[i]);

printf("\n");

return 0;

}

程序执行结果: 

Data:4167340692478586264

Result:0243441586264676978

【例512】在利用一维数组存储的一个升序数列中,使用折半查找法查找指定数值。1) 问题分析与算法设计查找是在一组数据中找指定的值。不同的数据对象有不同的查找方法。数据存储的结构不同,查找的方法也不同。为便于理解题意,先对查找的方法进行简要说明。对于给定的一组数,查找x的方法有多种,最简单的方法是顺序查找,就是从头开始依次与x进行比较,当找到x时查找成功,查找过程结束; 全部数据查找一遍,若未找到x,则查找失败,查找过程结束。顺序查找方法简单,但效率低。折半查找是一种高效的查找方法,它要在一个有序的数列中进行,具体方法如下。设有N个数据的数列已升序排序,存储在长度为N的一维数组a中,对查找范围内的数据设置3个特殊点,首位置为top,末位置为bot,中间位置为mid=(top bot)/2。查找初始,top为数组首元素的下标值0,bot为数组末元素的下标值N-1,mid为中间位置元素的下标值(N-1)/2。以下为查找过程: (1) 比较x与a[mid],若x与a[mid]相等,则查找成功,查找过程结束; 否则,若x>a[mid],则top=mid 1,若x

bot,则查找失败,查找过程结束; 否则,mid=(bot top)/2,转(1)。图59所示为在数列{3,6,7,8,21,23,36,38,67,69,80,82,85,88,96}中查找69时top、bot、mid的变化情况。该查找过程经过3次之后结束,查找成功。图59中的①、②、③是对3次查找所做的标识,表52列出了查找过程中各项数据的动态变化情况。

图59折半查找举例

表52折半查找过程动态信息表

查找过程本次查找范围及中间位置topbotmid本次查找结果下一次查找范围第1次0147a[mid]

x在mid之前,bot=mid-1第3次8109a[mid]==x查找结束

上述查找过程的算法NS图如图510所示。2) 实现程序程序如下: 

#include


#define N 15

int main()

{

int a[N]={3,6,7,8,21,23,36,38,67,69,80,82,85,88,96};

int x,top,bot,mid;

printf("Input x: "); 

scanf("%d",&x);        /*输入要查找的数值*/

top=0;             /*设置初始查找边界*/

bot=N-1;

do

{

mid=(top bot)/2;  /*确定中间位置*/

if(a[mid]==x)

break;        /*找到x 后终止循环*/

else if(a[mid]


top=mid 1;    /*确定后半段为下一次查找的范围*/

else

bot=mid-1;   /*确定前半段为下一次查找的范围*/

}while(top<=bot);

if(bot


printf("%d: no found.\n",x);

else

printf("Success!  a[%d] is %d.\n",mid,x);

return 0;

}

图510折半法查找x的算法NS图

程序执行结果: 

Input x: 69 (第1次执行,在数列中查找69)

Success!a[9] is 69.(查找成功,元素a[9]是69)

Input x: 33 (第2次执行,在数列中查找33)

33: no found.(在数列中未找到其值是33的数据)

在数列中查找33时,经过4次查找之后,循环条件top<=bot不再成立,查找过程结束。【例513】输入一个字符串,统计其中单词的个数。1) 问题分析与算法设计在一个字符串中,把由空格分隔的一组连续字符视为一个单词,例如字符串“  This is  a c   program.”有5个单词。分辨单词是对文本进行识别的基本操作。该“单词统计”问题是字符型数组的一种典型应用。如果要统计单词的个数,首先需要把单词找出来,这是进行单词统计最关键的过程。设长度是n的字符串含有的单词个数为word,该字符串存储在字符数组text中,各字符元素分别为text[0]、text[1]、text[2]、…、text[n-1]。对于text,有以下3种情况。第1种情况: text为空串,即没有任何输入字符,text[0]= \0,word的统计结果为0。第2种情况: text的开始字符为空格符,即text[0]= ,开始时没有出现单词,word的初值置为0。第3种情况: text的开始字符为非空格符,一开始就出现了单词,word的初值置为1。对于非空字符串,设置word初值之后按以下步骤检测下一个单词: ①  若text[i](i初值为1)不是字符串结束标记\0,当满足下列条件时必然出现新单词: 

text[i-1]== &&text[i]!=

② i ,重复以上步骤,直到text[i]为\0时结束。2)  实现程序程序如下: 

#include


#define N 100

int main()

{

char text[N];               /*设输入的字符串长度小于N*/

int word=0,i; 

printf("String: ");

gets(text);

if(text[0]!=\0)           /*text为非空字符串时进行单词统计*/ 

{

if(text[0]!= )
word=1;   /*text首字符是非空格符,置word为1*/

for(i=1;text[i]!=\0;i )

if(text[i-1]== &&text[i]!= )/*满足条件时遇到单词*/

word ; 

}

printf("Result: %d\n",word);

return 0;

}

程序执行结果: 

String:   This is  a c   program.  (字符串首字符为空格符)

Result:5

String: This is   (字符串首字符不是空格符)

Result:2

String:   (直接按回车键,输入空串)

Result:0

【例514】一个班级有N名学生,每个学生有两门课程,实行百分制考核,要求分别统计各个等级的人数,并将分等级统计的结果保存到一维数组中。分等级标准与第4章的例49相同。1) 问题分析与算法设计第4章的例49讨论了“学生成绩分等级统计”问题,实现了一个班级学生成绩的分等级统计,各个等级的统计结果分别用简单变量r0、r1、r2、r3、r4存储。若改用一维数组存储统计结果,程序会更简洁。(1) 学生成绩分为5个等级,因此可定义长度为5的int型数组r,每个数组元素存储一个等级的统计结果。在例49的基础上改写程序时应将存储统计结果的各个简单变量r0、r1、r2、r3、r4修改为相应的数组元素,其对应关系如表53所示。

表53统计变量对照表

对应项优秀人数良好人数中等人数及格人数不及格人数存储各等级人数的数组元素r[0]r[1]r[2]r[3]r[4]在例49程序中使用的变量r0r1r2r3r4

(2) 上述步骤完成后,各个等级的统计结果存储到数组r中,将其各个元素输出即可。2) 实现程序程序如下: 

#include


#define N 6    
  /*班级人数*/

int main()

{

int s1,s2,ave,i;

static int r[5]; 

for(i=0;i

{

printf("Score: ");

scanf("%d,%d",&s1,&s2);  /*输入课程成绩s1、s2*/

ave=(s1 s2)/2;           /*计算平均成绩*/

if(ave>=90) r[0] ;      /*优秀人数统计*/

else if(ave>=80) r[1] ; /*良好人数统计*/

else if(ave>=70) r[2] ; /*中等人数统计*/

else if(ave>=60) r[3] ; /*及格人数统计*/

else r[4] ;             /*不及格人数统计*/

}

for(i=0;i<5;i )            /*输出存储在数组r中的统计结果*/

printf("%d  ",r[i]);

printf("\n");

return 0;

}

该程序使用一维数组r存储统计结果,优秀、良好、中等、及格、不及格等各等级人数分别存储在r[0]到r[4]中。程序中定义保存统计结果的数组r时使用了“static int r[5];”语句,其中的static用于定义变量的存储类型,其作用是在r数组使用前清零,即将其各个元素初始化为0。有关变量存储类型的知识在第6章进行介绍。上述“学生成绩分等级统计”问题实现了一个班级学生成绩的分等级统计。以下是更进一步的问题,可以实现多个班级学生成绩的分等级统计。【例515】某年级共有3个班级,每班有N名学生,开设两门课程,要求分别对每个班级按照学习成绩进行分类统计,并将统计结果保存到一个二维数组中。1) 问题分析与算法设计该问题与上述“学生成绩分等级统计”问题的主要区别有两点: (1) 对多个班级分别统计。(2) 统计结果用二维数组保存。对于第1点,可以在“学生成绩分等级统计”的基础上增加一个外重循环来实现。对于第2点,需要定义一个二维数组,存储数据的形式如表54所示。

表54统计结果示意表

班级优秀人数良好人数中等人数及格人数不及格人数1班2班3班表中空白处是要统计存储的数据,因此可以定义一个3×5的二维数组r来保存统计结果,每个班级的统计结果存储在数组的一行上。例如,在对第1个班级进行统计时,优秀、良好、中等、及格和不及格这5个等级的统计结果应分别使用数组元素r[0][0]、r[0][1]、r[0][2]、r[0][3]和r[0][4]进行统计。2) 实现程序程序如下: 

#include


#define N 6    
  /*设每班有6名学生*/

int main()

{

int s1,s2,ave,i,j;

static int r[3][5];    /*定义保存统计结果的二维数组*/

for(i=0;i<3;i )       /*共有3个班级,用i控制*/

{

for(j=1;j<=N;j )   /*每个班级有N个学生,用j控制*/

{

printf("Class %d score%d: ",i 1,j);   /*友好提示*/

scanf("%d,%d",&s1,&s2);  /*输入一个学生的两门课程成绩*/

ave=(s1 s2)/2; 

if(ave>=90) r[i][0] ;      /*i班优秀人数统计*/

else if(ave>=80) r[i][1] ;  /*i班良好人数统计*/

else if(ave>=70) r[i][2] ;  /*i班中等人数统计*/

else if(ave>=60) r[i][3] ;  /*i班及格人数统计*/

else r[i][4] ;              /*i班不及格人数统计*/

}

}

for(i=0;i<3;i ) /*逐行输出各个班级的统计结果*/

{

for(j=0;j<5;j )  /*输出一个班级的各等级统计结果*/

printf("%5d",r[i][j]);

printf("\n");

}

return 0;

}

程序执行结果: 

Class 1 score1: 87,91 (输入1班的第1组数据)

Class 1 score2: 67,82 (输入1班的第2组数据)

Class 1 score3: 56,72 (输入1班的第3组数据)

Class 1 score4: 90,86 (输入1班的第4组数据)

Class 1 score5: 92,89 (输入1班的第5组数据)

Class 1 score6: 88,77 (输入1班的第6组数据)

Class 2 score1: 67,87 (输入2班的第1组数据)

Class 2 score2: 86,94 (输入2班的第2组数据)

Class 2 score3: 51,61 (输入2班的第3组数据)

Class 2 score4: 79,82 (输入2班的第4组数据)

Class 2 score5: 66,60 (输入2班的第5组数据)

Class 2 score6: 77,88 (输入2班的第6组数据)

Class 3 score1: 71,81 (输入3班的第1组数据)

Class 3 score2: 57,69 (输入3班的第2组数据)

Class 3 score3: 87,81 (输入3班的第3组数据)

Class 3 score4: 88,79 (输入3班的第4组数据)

Class 3 score5: 67,69 (输入3班的第5组数据)

Class 3 score6: 76,78 (输入3班的第6组数据)

13110(1班各等级统计结果)

12111(2班各等级统计结果)

02220(3班各等级统计结果)程序的输出结果共有3行数据,分别是3个班级的学生成绩分等级统计情况,每列的数据由前到后依次为优秀、良好、中等、及格和不及格的人数。上述程序讨论的是各班级学生人数相同的情况,在通用性方面还有欠缺,可以进一步完善程序,使其适用于各班级人数不同的情况。例如可以定义一个一维数组cla,用来存放各个班级的人数,在输入一个班级的学生成绩时用cla的相应元素值作为成绩输入的循环控制量即能满足上述要求。下面是实现程序(略去的代码是没有变动的程序段): 

#include


int main()

{

int s1,s2,ave,i,j;

static int r[3][5]; 

int cla[3];       /*数组各元素存储各个班级的人数*/

for(i=0;i<3;i )   /*输入各个班级的人数*/

scanf("%d",&cla[i]);

for(i=0;i<3;i )

{

for(j=1;j<=cla[i];j )

{



}

}  



}

【例516】打印杨辉三角形的前N行数据。1) 问题分析与算法设计杨辉三角形是中国古代数学研究的一项重要发现,它是一个数值组合图形,借助杨辉三角形能求解二项式问题。杨辉三角形的结构如下: 11112113311464115101051若用y(i,j)表示杨辉三角形中第i行、第j列的数值,则有: y(i,j)=1(j=1或j=i)

y(i-1,j-1) y(i-1,j)(j≠1且j≠i)根据杨辉三角形的数据排列特点,可以使用一个N行N列的二维数组y存储杨辉三角形,这样求解杨辉三角形的问题即转化为数组元素值的计算问题。由于二维数组的行、列起始下标均从0开始,则对于第i行、第j列的元素y[i][j]应按以下公式求值: y[i][j]=1(j=0或j=i)

y[i-1][j-1] y[i-1][j](j≠0且j≠i)在编程实现时只使用y数组的下三角形元素,利用杨辉三角形每行首、尾元素为1的特性对y数组赋初值,其他元素经计算获得。2) 实现程序程序如下: 

#include


#define N 6

int main()

{

int i,j,y[N][N];

printf("\n");

for(i=0;i


{

y[i][0]=1;

y[i][i]=1;

}

for(i=2;i


for(j=1;j

y[i][j]=y[i-1][j-1] y[i-1][j];

for(i=0;i


{

for(j=0;j<=i;j )

printf("%5d",y[i][j]);

printf("\n");

}

return 0;

}

● 问题思考杨辉三角形的呈现形式有多种,以下是一种较典型的杨辉三角形形式: 1

11

121

1331

14641

15101051怎样修改上述程序使其输出该形式的杨辉三角形?小结本章介绍了关于数组的程序设计知识,主要有数组的特点、数组的定义及使用方法、数组在实际问题中的应用等,一维数组是本章的重点内容。(1) 数组是一种重要的数据结构,适合同类型批量数据的存储处理,因此在实际问题中有广泛的应用。(2) 数组与循环结构关系密切,数组的输入与输出、数组元素的运算等通常在循环体中进行,熟练掌握循环控制结构是进行数组程序设计的基础。(3) 数组分为一维数组、二维数组等,一维数组的定义格式为“数据类型数组名[数组长度]”,二维数组的定义格式为“数据类型数组名[数组行数] [数组列数]”。无论数组的维数如何,每一维的下标值都从0开始。(4) 数组初始化的方式有多种,可以对全部元素初始化,也可以对部分元素初始化。对于一维数组,若对全部元素初始化,数组长度说明可以省略; 对于二维数组,只允许省略行数的说明,在任何情况下都不能省略对列数的说明。(5) 字符型数组用于存储字符串,它具有数组的一切性质,但又有其特点。字符型数组的操作对应大量的操作函数,例如字符串的复制、连接、比较等均有专门的函数予以实现。(6) 数组的存储占用连续的内存空间,占用的存储单元数由数组长度和数组的数据类型确定。一维数组从首元素开始依次存储,二维数组的每一行是一个一维数组,在存储时从首行开始按行依次存储。数组名代表数组的首地址,该性质在后续程序设计中被广泛应用。(7) 本章最后通过几个典型实例,系统介绍了数组应用程序的设计方法和过程。习题五一、 选择题1.  若有数组定义int m[][2]={1,3,5,7,9},则以下叙述正确的是。

A.  该定义存在语法错误B.  该定义等价于int m[3][2]={1,3,5,7,9}C.  该定义等价于int m[][2]={{1,3,5},{7,9}}D.  该定义等价于int m[2][2]={1,3,5,7,9}2.  对两个数组a和b进行如下初始化: 

char a[]={a,,c,d,e,f};

char b[]="abcdef";

则以下叙述正确的是。A.  a数组与b数组完全相同B.  a数组与b数组具有相同的长度C.  a数组和b数组的最后一个字符都是字符串结束标识符\0D.  a数组的长度比b数组的长度小3.  有程序段如下: 

int a[5],i;

for(i=0;i<5;i )

scanf("%d",&a[i]);

要求通过键盘为数组a输入数据,在下面给出的数据输入形式中会使a数组获得无效数据。A.   30 90 70 20 60 B. 30,90,70,20,60 C.   30 90 70 D. 30 90 70 20 60 

20 60 4.  有定义如下: 

int a[10]={3,5,7,9,8,4,21,10,6,15},t;

要求将数组的首、尾元素交换,以下交换方式正确的是。A.  a[0]=a[9],a[9]=a[0];B.  t=a[1],a[1]=a[10],a[10]=t;C.  t=a[10],a[10]=a[1],a[1]=t;D.  t=a[0],a[0]=a[9],a[9]=t; 5.  以下数组定义正确的是。A.  int n=10,a[n];B.  int a[3][]={1,2,3,4,5,6,7};C.  char name[20]="liming";D.  int name[20]="liming"; 6.  下列是关于字符数组的描述,错误的是。A.  字符数组可以存放字符串B.  字符数组中的字符串可以整体输入与输出C.  在定义字符数组时可以使用一个字符串对其初始化D.  可以用关系运算符对字符数组中的字符串进行比较7.  下列程序执行后的输出结果是。

#include


#include


int main()

{

char arr[2][4];

strcpy(arr[0],"you");

strcpy(arr[1],"me");

arr[0][3]=&;

printf("%s\n",arr[0]);

return 0;

}

A.   youB. meC. you&meD. me&you8.  在下列程序段中不能输入字符串的是。A.  char str[10];

puts(gets(str));B. 

char str[10];

scanf("%s",str);C. char str[10];

gets(str);

D. char str[10];

getchar(str);

9.  以下是一个字符串输入与输出程序: 

#include


#include


int main()

{

char ch1[10],ch2[10];

gets(ch1);

gets(ch2);

if(strcmp(ch1,ch2)>0)

puts(ch1);

else

puts(ch2);

return 0;

}

下列关于程序功能的描述正确的是。A. 输入两个字符串,然后按照输入顺序依次输出这两个字符串B. 输入两个字符串,将其中的大字符串输出C. 输入两个字符串,将其中的小字符串输出D. 输入两个字符串,将其中最长的字符串输出10.  以下是一个字符串输入与输出程序: 

#include


#include


int main()

{

char ch1[10],ch2[10];

gets(ch1);

gets(ch2);

if(strlen(ch1)>strlen(ch2))

puts(ch1);

else

puts(ch2);

return 0;

}

下列关于程序功能的描述正确的是。A. 输入两个字符串,将其中的大字符串输出B. 输入两个字符串,将其中的小字符串输出C. 输入两个字符串,将其中的长字符串输出D. 输入两个字符串,将其中的短字符串输出二、 程序分析题1.  下面程序的功能是输出数组s中最大元素的下标。在横线处填上适当的内容,使它能得出正确的结果。

#include


int main()

{

int k,p,s[]={1,-9,7,2,-10,3};

for(p=0,k=p;p<6;p )

if(s[p]>s[k]);

printf("%d\n", k);

return 0;

}

2.  下面程序的功能是将一个字符串中的小写英文字母全部改成大写形式,然后输出。在横线处填上适当的程序代码,使它能输出正确的结果。

#include


int main()

{

int i=0;

char str[80];

scanf("%s",str);

while (①)

{

if(②) str[i]= str[i]-32;

③;

}

printf("%s\n", str); 

return 0;

}

3.  下列程序的输出结果是。

#include


int main()

{

int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};

int i,j,s=0;

for(i=1;i<3;i )

for(j=0;j

s =a[i][j];

printf("%d\n",s);

return 0;

}

4.  下面的程序将二维数组a的行元素和列元素互换,然后存储到另一个二维数组b中。在横线处填上合适的程序代码。

#include


int main()

{

int a[2][3]={{10,20,30},{40,50,60}};

int b[3][2],i,j;

for(i=0;i<=1;i )

{

for(j=0;j<=2;j )

{

printf("%5d",a[i][j]);

;

}

printf("\n");

}

for(i=0;i<=2;i )



for(j=0;j<=1;j )

printf("%5d",b[i][j]);

printf("\n");

}

return 0;

}

三、 编程题1.  请回忆最近5次的网购情况,凡是超值的记为1,其他记为0,如表55所示。编写程序,将超值记录数据按照网购顺序依次输入到一维数组中,然后判断输出网购评价结果。

表55超值网购评价表

网购顺序第1次第2次第3次第4次第5次超值记录10110

对应表55的网购数据将输出以下结果。

第1次,超值

第2次,一般

第3次,超值

第4次,超值

第5次,一般

2.  将Fibonacci数列前20项中的偶数值找出来,存储到一维数组中。3.  某就业指导网站对20个行业进行了网络调查,并发布了每个行业的平均月薪。试编写程序,将这些数据输入到一维数组中,然后将超过平均值的数据单独存储到一个高收入行业数组中。4.  某研究小组共有6人,每个人的年龄按照从小到大的顺序存储到一维数组中。要求编写程序,计算他们的平均年龄,并确定出最接近且不大于平均年龄的那个值在数组中的位置。5.  某研究团队共有N个成员,将每个人的年龄随机存储到一维数组中。要求编写程序,计算他们的平均年龄,并确定出最接近且不大于平均年龄的那个值。6.  回文是顺读和倒读都一样的字符串,例如ASDFDSA是回文,而ASDFDAS不是回文。输入一个字符串,判断其是否为回文。若是回文,输出Yes,否则输出No。7.  将一行电文译成密码,规律如下: (1) 将a、b、c、…、z这26个小写字母分别译成0、1、…、9、a、b、c、d、e、f、g、h、i、@、#、$、%、&、!、*; (2) 将空格译成j; (3) 其他字符不变。8.  打印点阵图案。先用纸笔绘制一个N行N列的点阵图案,并将图案信息输入到一个二维数组中(例如,黑点用1表示,空白点用0表示),然后将图案打印出来(例如,对应1打印*,对应0打印空格)。9.  某人发表英文短文一篇,共有5段,每段不超过80个字母,每个字符(不含空格)计稿费0.5,编程计算共获得多少稿费。10.  用一维数组作为存储结构,实现Josephus环报数游戏。以下是对Josephus环报数游戏的描述: 有n个人围成一圈,从1开始顺序编号到n,首先自1号开始顺时针从1报数,数到m者自动出列,然后自下一个人开始重新从1报数,数到m者仍然自动出列,直到最后一个人出列为止。编写程序,输出这n个人出列的顺序。11.  鞍点问题。在二维数组中,若某一位置的元素在该行中最大,而在该列中最小,则该元素即为该二维数组的一个鞍点。要求从键盘输入一个二维数组,当鞍点存在时把鞍点找出来。




































































C语言程序设计(第4版) pdf下载声明

本pdf资料下载仅供个人学习和研究使用,不能用于商业用途,请在下载后24小时内删除。如果喜欢,请购买正版

pdf下载地址

版权归出版社和作者所有,下载链接已删除。如果喜欢,请购买正版!

链接地址:C语言程序设计(第4版)