- C语言程序开发范例宝典(软件工程师典藏版)
- 杨丽 郭锐 陈雪峰编著
- 691字
- 2020-06-27 10:54:40
实例024 选票统计
这是一个可以提高基础技能的实例
实例位置:光盘\mingrisoft\01\024
实例说明
班级竞选班长,共有3个候选人,输入参加选举的人数及每个人选举的内容,输出3个候选人最终的得票数及无效选票数。运行结果如图1.24所示。
图1.24 选票统计
技术要点
本实例是一个典型的一维数组应用,这里主要说一点就是C语言规定只能逐个引用数组元素而不能一次引用整个数组,本程序这点体现在对数组元素进行判断时只能通过for语句对数组中的元素一个一个地引用。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)定义数组及变量为基本整型。
(4)输入参加选举的人数,再输入每个人的选举内容并将其存入数组中。对存入数组中的元素进行判断,统计出各个候选人的票数和无效的票数。
(5)将最终统计出的结果输出。
(6)主要程序代码如下:
main() { int i, v0 = 0, v1 = 0, v2 = 0, v3 = 0, n, a[50]; printf("please input the number of electorate:\n"); scanf("%d",&n); /*输入参加选举的人数*/ printf("please input 1or2or3\n"); for(i = 0; i < n; i++) scanf("%d",&a[i]); /*输入每个人所选的人*/ for(i = 0; i < n; i++) { if(a[i] = = 1) v1++; /*统计1号候选人的票数*/ else if(a[i] = = 2) v2++; /*统计2号候选人的票数*/ else if(a[i] = = 3) v3++; /*统计3 3号候选人的票数*/ else v0++; /*统计无效票数*/ } printf("The Result:\n"); printf("candidate1:%d\ncandidate2:%d\ncandidate3:%d\nonuser:%d\n", v1, v2, v3,v0); /*将之中统计的结果输出*/ }
举一反三
根据本实例,读者可以:
已有一个已经排好序的数组,现输入一个数,要求按原来的规律将它插入数组中。
输入5名学生的4门课程(数学、概率、线性代数、c语言)的成绩,然后打印每个学生的各科成绩,及每个学生的总分和平均分。
实例025 模拟比赛打分
本实例可以方便操作、提高效率
实例位置:光盘\mingrisoft\01\025
实例说明
首先从键盘中输入选手人数,然后输入对每个选手裁判的打分情况,这里假设裁判有5位,在输入完以上要求内容后,输出每个选手的总成绩。运行结果如图1.25所示。
图1.25 模拟比赛打分
技术要点
程序中使用了嵌套的for循环,外层的for循环是控制选手变化的,内层for循环是控制5个裁判打分情况的,这里要注意由于不知道选手的人数,所以存储裁判所打分数的数组的大小是随着选手人数变化的。因为有5个裁判,所以当数组下标能被5整除时则跳出内层for循环,此时计算出的总分是5名裁判给一名选手打分的结果,将此时计算出的总成绩存到另一个数组中。输出选手成绩时也是遵循上面的规律。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <string.h> #include <stdio.h>
(3)从键盘中输入选手人数及裁判给每个选手打分的情况,输入的分数存在数组a中,统计出每个选手所得的总分并存到数组b中,最终将统计出的结果按指定格式输出。
(4)主函数程序代码如下:
main() { int i, j = 1, n; float a[100], b[100], sum = 0; printf("\nEnter the number of players:\n"); scanf("%d",&n); /*从键盘中输入选手的人数*/ for(i = 1; i <= n; i++) { printf("now player %d\n", i); printf("please input score:\n"); for(; j < 5 *n + 1; j++) { scanf("%f",&a[j]); /*输入5个裁判每个裁判所给的分数*/ sum+=a[j]; /*求出总份数*/ if(j % 5==0) /*一位选手有5个裁判给打分*/ break; } b[i]=sum; /*将每个选手的总分存到数组b中*/ sum=0; /*将总分重新置0*/ j++; /*j自加*/ } j = 1; printf("player judgeA judgeB judgeC judgeD judgeE total\n"); for(i = 1; i <= n; i++) { printf("player %d",i); /*输出几号选手*/ for(; j < 5 *n + 1; j++) { printf("%8.1f",a[j]); /*输出裁判给每个选手对应的分数*/ if(j % 5 = = 0) break; } printf("%8.1f\n",b[i]); /*输出每个选手所得的总成绩*/ j++; } }
举一反三
根据本实例,读者可以:
在本实例的基础上改进程序,即去掉每个选手所得的最高分和最低分,计算出选手的平均得分。
输入学生3门功课成绩,并找出每门功课的最高分及不及格人数。
实例026 对调最大与最小数位置
这是一个可以提高分析能力的实例
实例位置:光盘\mingrisoft\01\026
实例说明
从键盘中输入一组数据,找出这组数据中最大数与最小数,将最大数与最小数位置互换,将互换后的这组数据再次输出。运行结果如图1.26所示。
图1.26 对调最大与最小数位置
技术要点
本实例的主要思路如下:首先是要确定最大数与最小数的具体位置,将a[0]赋给min,用min和数组中其他元素比较,有比min小的,则将这个较小的值赋给min,同时将其所在位置赋给j,当和数组中元素均比较一次后,此时j中存放的就是数组中最小元素所在的位置。最大元素位置的确定同最小元素。当确定具体位置后将这两个元素位置互换,最后将互换后的数组输出。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件并进行宏定义:
#include <stdio.h>
(3)利用for循环和if条件判断语句确定出最大值与最小值的位置,互换后将数组再次输出。
(4)主函数程序代码如下:
main() { int a[20],max,min,i,j,k,n; /*定义数组及变量数据类型为基本整型*/ printf("please input the nunber of elements:\n"); scanf("%d",&n); /*输入要输入的元素个数*/ printf("please input the element:\n"); for(i=0;i<n;i++) /*输入数据*/ scanf("%d", &a[i]); min = a[0]; for(i=1;i<n;i++) /*找出数组中最小的数*/ if(a[i] < min) { min = a[i]; j=i; /*将最小数所存储的位置赋给j*/ } max = a[0]; for(i=1;i<n;i++) /*找出这组数据中的最大数*/ if(a[i] > max) { max = a[i]; k=i; /*将最大数说存储的位置赋给k*/ } a[k]=min; /*在最大数位置存放最小数*/ a[j]=max; /*在最小数位置存放最大数*/ printf("\nthe position of min is:%3d\n",j); /*输出原数组中最小数所在的位置*/ printf("the position of max is:%3d\n",k); /*输出原数组中最大数所在的位置*/ printf("Now the array is:\n"); for(i = 0; i < n; i++) printf("%5d",a[i]); /*将还完位置的数组再次输出*/ }
举一反三
根据本实例,读者可以:
从键盘中输入10个元素,要求将位置互换,即第一个元素与最后一个元素互换,第二个元素与倒数第二个元素互换,依此类推,将互换后的数组再次输出。
从键盘中输入10个元素,要求每隔一个元素进行输出。
实例027 使用数组统计学生成绩
这是一个可以提高基础技能的实例
本实例可以方便操作、提高效率
实例位置:光盘\mingrisoft\01\027\ in ris ft\01\031
实例说明
输入学生的学号及语文、数学、英语成绩,输出学生各科成绩信息及平均成绩。运行结果如图1.27所示。
图1.27 统计学生成绩
技术要点
(1)本实例的关键是用输入的学生数量来控制循环次数的,也就是有多少学生就输入多少次三科成绩。输出成绩等相关信息时依旧是根据学生数量来控制循环次数的。
(2)实例中用到符号常量MAX。以下是和符号常量相关的知识。
● 符号常量不同于变量,它的值在其作用域内不能改变,也不能再被赋值。
● 使用符号常量的好处是在需要改变一个常量时能做到“一改全改”,要想把学生数量限制在100以内,只需将程序开始处的#define MAX 50改成 #define MAX 100就可以了,不需将程序的每一处都改到。
注意:程序中定义average数组是单精度型,所以在输出时要以%f形式输出,实例中是以%8.2f形式输出,具体含义是输出的数据占m列,其中有n位小数。如果数字长度小于m,则左端补空格。%8ld和%8d含义与此相似,即如果数据的位数小于8,则左端补以空格,若大于8,则按实际位数输出。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件并进行宏定义:
#include<stdio.h> #define MAX 50 /*定义MAX为常量50*/
(3)定义变量及数组的数据类型。
(4)输入学生数量。
(5)输入每个学生学号及三门学科的成绩。
(6)将输入的信息输出并同时输出每个学生三门学科的平均成绩:
main() { int i,num; /*定义变量i,num为基本整型*/ int Chinese[MAX],Math[MAX],English[MAX]; /*定义数组为基本整型*/ long StudentID[MAX]; /*定义StudentID为长整形*/ float average[MAX]; printf("please input the number of students"); scanf("%d",&num); /*输入学生数*/ printf("Please input a StudentID and three scores:\n"); printf(" StudentID Chinese Math English\n"); for(i=0;i<num;i++) /*根据输入的学生数量控制循环次数*/ { printf("No.%d>",i+1); scanf("%ld%d%d%d",&StudentID[i],&Chinese[i],&Math[i],&English[i]); /*依次输入学号及语文,数学,英语成绩*/ average[i]=(float)(Chinese[i]+Math[i]+English[i])/3;/*计算出平均成绩*/ } puts("\nStudentNum Chinese Math English Average"); for(i=0;i<num;i++) /*for循环将每个学生的成绩信息输出*/ { printf("%8ld %8d %8d %8d %8.2f\n",StudentID[i],Chinese[i],Math[i],English[i],average[i]); } }
举一反三
根据本实例,读者可以:
在本程序的基础上算出班级总平均分。
编程实现10人参加投篮比赛(按出场顺序排号),分别输入10人在3分钟内投中的次数,求出平均投中次数及投中次数最少与最多的运动员。
实例028 设计魔方阵
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\028
实例说明
打印5阶幻方即它的每一行、每一列和对角线之和均相等。运行结果如图1.28所示。
图1.28 打印5阶幻方
技术要点
本实例的技术要点是找出幻方中各数的排列规律,具体规律如下。
(1)将1放在第一行中间一列。
(2)从2开始直到25各数依次按下列规则存放:每一个数存放的行比前一个数的行数减1,列数加1。
(3)如果上一个数的行数为1,则下一个数的行数为5,列数加1。
(4)当上一个数的列数为5时,下一个数的列数应为1,行数减1。
(5)如果按上面步骤确定的位置上已经有数(本题中不为0),或者上一个数是第1行第5列时,则把下一个数放在上一个数的下面。
注意:实例中我们使用数组时下标从1到5(方便读者理解),所以数组的行列的长度应为6。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)定义变量及数组的数据类型。
(4)使用for语句按上面所讲规律往数组a中相应位置存放数据。
(5)用嵌套的for语句将二维数组a输出并且每输出一行进行换行。
(6)主要程序代码如下:
main() { int i,j,x=1,y=3,a[6][6]={0}; /*因为数组下标要用1到5,所以数组长度是6*/ for(i=1;i<=25;i++) { a[x][y]=i; /*将1到25所有数存到存到数组相应位置*/ if(x= =1&&y= =5) { x=x+1; /*当上一个数是第1行第五列时,下一个数放在它的下一行*/ continue; /*结束本次循环*/ } if(x==1) /*当上一个数是是第1行时,则下一个数行数是5*/ x=5; else x--; /*否则行数减1*/ if(y==5) /*当上一个数列数是第5列时,则下一个数列数是1*/ y=1; else y++; /*否则列数加1*/ if(a[x][y]!=0) /*判断经过上面步骤确定的位置上是否有非零数*/ { x=x+2; /*表达式为真则行数加2列数减1*/ y=y-1; } } for(i=1;i<=5;i++) /*将二维数组输出*/ { for(j=1;j<=5;j++) printf("%4d",a[i][j]); printf("\n"); /*每输出一行回车*/ } }
举一反三
根据本实例,读者可以:
在本程序的基础上编程实现输入n阶幻方,n的值由用户从键盘中输入。
先读入一个整数n(n<15),在读入一个n行n列的整数矩阵A,编程计算矩阵A的每一行的元素之和。
1.6 字符和字符串操作
字符型数据在计算机中占有非常重要的位置。在C语言中,字符型常量或字符型变量都是指一个单个的字符。需要处理连续多个字符时,要采用字符型数组或字符串常量。本节中的几个实例就是关于字符及字符串操作的。
实例029 字符串倒置
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\029
实例说明
输入字符串,要求将该字符串倒置输出。例如:输入abcdef,输出fedcba,运行结果如图1.29所示。
图1.29 字符串倒置
技术要点
本实例中用到了是strlen及gets函数,以下是关于这两个函数知识要点。
● strlen(字符数组):它是测试字符串长度的函数。函数的值为字符串中的实际长度,注意这里不包括“\0”在内。在使用strlen函数时要包含头文件“string.h”。
● gets(字符数组):从终端输入一个字符串到字符数组,并且得到一个函数值。与其相对应的是puts(字符数组),它的作用是将一个字符串输出到终端。
注意:实例中有这样一个语句j=strlen(s)-1,这里j为什么是strlen(s)-1而不是strlen(s)呢?原因如下:如果数组s中含有10个元素,那么它的长度为10,而s的数组下标从0开始那么下标的最大值为9,j这里是代表数组下标,所以开始时的最大值应是strlen(s)-1也就是9。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件并进行宏定义:
#include<stdio.h> #include<string.h> #define N 100 /*定义N为100*/ void convert(char s[N]) /*自定义函数,参数为字符型数组*/
(3)自定义函数convert,参数为字符型数组,该函数的作用是实现字符串的倒置,在实现倒置的过程中用到for语句,其中变量i的取值范围从0到strlen(s)/2-1,当数组长度偶数时实现字符串前半部分与后半部分相应位置的互换,当数组长度为奇数时字符串前半部分与后半部分相应位置互换,中间的字符位置不动。位置互换的过程中借助中间变量temp。
(4)主函数定义基本整型变量和字符型数组。
(5)用gets函数接收输入的字符串,调用前面定义的convert函数。
(6)主要程序代码如下:
void convert(char s[N]) { int i,j; /*定义变量i,j为基本整型*/ char temp; /*定义变量temp为字符型*/ for(i=0;i<strlen(s)/2;i++) /*使用for语句实现字符串位置倒置*/ { j=strlen(s)-1; /*长度减1因为数组起始坐标从0开始*/ temp=s[i]; s[i]=s[j-i]; s[j-i]=temp; } printf("Now string:\n%s",s); /*输出倒置后的字符串*/ } main() { int i; char str[N]; /*定义字符型数组*/ printf("Enter the string:\n"); gets(str); /*gets函数获得字符串*/ printf("Origin str:\n%s",str); convert(str); /*调convert函数*/ }
举一反三
根据本实例,读者可以:
编写一个译码和解码程序,译码规则如下。
(1)令密文中的l对应原文中的a,其余字母依次按顺序对应。
原文:a b c d e f g h i j k l…
密文:l m n o p q r s t u v w…
(2)其余非字母字符不变。
打印正弦曲线。
实例030 字符串替换
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\030
实例说明
编程实现将字符串“today is Monday”替换变成“today is Friday”,运行结果如图1.30所示。
图1.30 字符串替换
技术要点
本实例的算法思想如下:首先输入字符串1,再输入要替换的内容和替换的位置(字符串1中的位置),这时只需从替换位置开始将要替换的内容逐个拷到字符串1中,直到遇到字符串1的结束符或遇到替换字符串的结束符便结束替换。
注意:字符串1的位置从0开始。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)自定义replace()函数,作用是使用字符串2替换字符串1中指定位置开始的字符串。代码如下:
char*replace(char*s1,char*s2,int pos) /*自定义替代函数*/ { int i, j; i = 0; for(j=pos;s1[j]!='\0';j++) /*从原字符串指定位置开始替代*/ if(s2[i] != '\0') { s1[j]=s2[i]; /*将替代内容逐个放到原字符串中*/ i++; } else break; return s1; /*将替代后的字符按串输出*/ }
(4)主函数程序代码如下:
main() { char string1[100],string2[100]; /*定义两个字符串数组*/ int position; printf("\nPlease input original string:"); gets(string1); /*输入字符串1*/ printf("\nPlease input substitute string:"); gets(string2); /*输入字符串2*/ printf("\nPlease input substitute position:"); scanf("%d",&position); /*输入要替换的位置*/ replace(string1,string2,position); /*调用替换函数*/ printf("\nThe final string:%s\n",string1); /*输出最终字符串*/ }
举一反三
根据本实例,读者可以:
向一个整型数组中输入10个数据,输入要替换的位置和数据,进行数据替换。
不使用函数,编程实现字符串替换。
实例031 回文字符串
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\031
实例说明
回文字符串就是正读反读都一样的字符串,例如:“radar”,要求从键盘中输入字符串,判断该字符串是否是回文字符串。运行结果如图1.31所示。
图1.31 回文字符串
技术要点
如何判断输入的字符串是回文字符串,这里通过一个例子来说明一下。
假设字符串是kstsk,我们会发现该字符串有5个字符,但是用数组来存储这个字符串,最后一个字符对应的数组下标将会是4,我们首先对str[1]与str[3]进行判断,看这两个字符是否相等,若相等再接着判断str[0]与str[4]是否相等,若相等且此时已经判断到最外层,则说明该数相对位置上的字符相同,是回文字符串,否则不是回文字符串。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)自定义palind()函数,作用是检测输入的字符串是否是回文字符串。代码如下:
int palind(char str[],int k, int i) /*自定义函数检测是否为回文字符串*/ { if(str[k]==str[i-k]&&k==0) /*递归结束条件*/ return 1; else if(str[k]==str[i-k]) /*判断相对应的两个字符是否相等*/ palind(str,k-1,i); /*递归调用*/ else return 0; }
(4)主函数程序代码如下:
main() { int i=0,n=0; /*i记录字符个数,n是函数返回值*/ char ch,str[20]; while((ch=getchar())!='\n') { str[i]=ch; i++; } if(i%2==0) /*当字符串中字符个数为偶数时*/ n=palind(str,(i/2),i-1); else n=palind(str,(i/2-1),i-1); /*当字符串中字符个数为奇数时*/ if(n= =0) printf("not palindrome"); /*当n为0说明不是回文数,否则是回文数*/ else printf("palindrome"); getch(); }
举一反三
根据本实例,读者可以:
编程实现以下问题:很早以前有个很凶狠的奴隶主,以杀奴隶为乐。他有很多的奴隶,一天,他让37个奴隶围成一圈,编上号码(1~37),第一个奴隶从1开始数,数到5的那个人被杀掉,接下来后面的那个人又从1开始数,数到5的那个人也被杀掉……最后剩下一个奴隶,这个奴隶可以活下来不杀,问最后是哪个奴隶可以活下来?
输入一篇文章,文章中每两个单词之间用空格区分,统计单词中回文的个数。
实例032 不用strcat连接两个字符串
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\032
实例说明
连接两个字符串,要求不使用strcat函数。运行结果如图1.32所示。
图1.32 连接两个字符串
技术要点
(1)本实例的关键技术要点是在将后一个字符串连接到前一个字符串的时候要先判断前一个字符串的结束标志在什么位置,只有找到了前一个字符串的结束标志才能连接后一个字符串。
(2)程序中用到了字符数组的输入/输出,这里详细介绍一下。
● 字符数组可以逐个字符输入/输出,用格式字符“%c”输入或输出一个字符。
● 字符数组也可将整个字符串一次输入或输出。用“%s”格式符。这里重点介绍这种方法。
(1)用“%s”格式符输出字符串时,printf函数中的输出项是字符数组名,而不是数组元素名。scanf函数中的输入项是也是字符数组名且不要加&,因为在c语言中规定数组名代表数组的起始地址。如本实例中代码:
scanf("%s",a); printf("%s",a);
(2)如果数组长度大于字符串实际长度,也只输出到遇“\0”结束。如本实例中数组a的长度为100,我们两个字符串合并后的长度往往也达不到100,在输出的时候只输出到遇“\0”时的字符,并不是将100个字符都输出。
(3)如果一个字符数组中包含一个以上“\0”,则遇第一个“\0”时输出就结束。如本实例中在输入第二个字符串时若输入的不是world而是beautiful world,注意这里的beautiful和world之间有空格,那么最终合并出的字符串将是hellobeautiful而不是hellobeautiful world,因为只将空格前的字符送到b中,由于把beautiful作为一个字符串处理,因此在其后加“\0”。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)定义变量i、j为基本整型,数组a和b为字符型。
(4)用scanf函数分别将字符串1和字符串2送到a和b数组中。
(5)用第一个while语句找出数组a中的第一个结束标志,找到后将b数组中的元素接着放到a数组中直到遍历到b数组中的第一个结束标志结束第二个循环,最后在新合并成的字符串后加“\0”。
(6)主要程序代码如下:
main() { int i=0,j=0; /*定义整型变量*/ char a[100],b[50]; /*定义字符型数组*/ printf("please input string1:\n"); scanf("%s",a); /*输入字符串存于数组a中*/ printf("please input string2:\n"); scanf("%s",b); /*输入字符串存于数组b中*/ while(a[i]!='\0') /*逐个遍历数组a中的元素,直到遇到字符串结束标志*/ i++; while(b[j]!='\0') /*逐个遍历数组b中的元素,直到遇到字符串结束标志*/ a[i++]=b[j++]; /*将数组b中的元素存入数组a中并从数组a原来存放'\0'位置开始,覆盖'\0'*/ a[i]='\0'; /*在合并后的两个字符串的最后加'\0'*/ printf("%s",a); /*输出合并后的字符串*/ }
举一反三
根据本实例,读者可以:
编写一个程序,将两个字符串s1和s2比较,如果s1>s2,输出一个正整数;如s1=s1,输出0;如s1<s2,输出一个负数。要求不使用strcmp函数。
编程实现将数字转换为字符串。
实例033 删除字符串中连续字符
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\033
实例说明
输入一个字符串,输入要删除的位置及长度,输出进行完删除操作的字符串。运行结果如图1.33所示。
图1.33 删除字符串中连续字符
技术要点
(1)本实例的关键技术是如何实现删除,这里我们采用的方法是将要删除部分后面字符从要删除部分开始逐个覆盖。
(2)在确定从哪个字符开始删除的时候读者要尤其注意,例如本实例中字符串hello world,字母e是这个字符串中的第2个字符但在数组s中它的下标却是1,也就是说如果用户输入的position是2,那么我们开始进行覆盖的位置在数组s中的下标就应是position-1,同理我们可以确定出将用哪个字符开始覆盖。在本实例中也就是i的初值,通过上面的分析我们就很容易确定它在数组中的具体位置即postion-1+length。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)自定义删除函数,该函数有3个参数。
(4)用for语句实现将删除部分后的字符依次从删除部分开始覆盖。最终将新得到的字符串返回。
(5)主函数编写,先定义字符型数组及两个基本整型变量,通过输入函数分别获得字符串、position及length的值,调用del函数,最终将新得到的字符串输出。
(6)主要程序代码如下:
char del(char s[],int pos,int len) /*自定义删除函数*/ { int i; for(i=pos+len-1;s[i]!='\0';i++,pos++) /*i初值为指定删除部分后的第一个字符*/ s[pos-1]=s[i]; /*用删除部分后的字符依次从删除部分开始覆盖*/ s[pos-1]='\0'; /*在重新得到的字符后加上字符串结束标志*/ return s; /*返回新得到的字符串*/ } main() { char str[50]; /*定义字符型数组*/ int position; int length; printf("\nPlease input string:"); gets(str); /*使用gets函数获得字符串*/ printf("\nPlease input delete position:"); scanf("%d",&position); /*输入要删除的位置*/ printf("\nPlease input delete length:"); scanf("%d",&length); /*输入要删除的长度*/ del(str,position,length); /*调用删除函数*/ printf("\nThe final string:%s",str); /*将新得到的字符串输出*/ }
举一反三
根据本实例,读者可以:
将一个任意长度的字符串中的字符按照ASCII码表从小到大的顺序重新排列形成一个新的字符串并打印出来。
将输入的字符串str下标值为偶数的元素由小到大重新排列并输出在屏幕上。
实例034 字符升序排列
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\034
实例说明
将已按升序排好的字符串数组a和字符串数组b按升序归并到字符串数组c中并输出。运行结果如图1.34所示。
图1.34 字符串升序排列
技术要点
本实例的算法思想如下:因为输入的字符串数组a和数组b是有序字符串,所以对数组a和数组b中的元素逐个比较,哪个字符小哪个字符就先放到数组c中,直到数组a或数组b中有一个字符串全部放到数组c中,再判断数组a和数组b哪一个字符串全部复制到数组c中,对未将字符串全部复制到数组c中的字符串,从未复制的位置开始将后面字符串全部连接到数组c中。这样就完成将字符串数组a和字符串数组b按升序归并到字符串数组c中。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)用while循环将至少一个字符串全部复制到c中,用if条件语句进行判断,将未复制到c中的字符串连接到c中。
(4)主函数程序代码如下:
main() { char a[100],b[100],c[200], *p; int i = 0, j = 0, k = 0; printf("please input string a:\n"); scanf("%s",a); /*输入字符串1放入a数组中*/ printf("please input string b:\n"); scanf("%s",b); /*输入字符串2放入b数组中*/ while(a[i] != '\0' && b[j] != '\0') { if(a[i]<b[j]) /*判断a中字符是否小于b中字符*/ { c[k]=a[i]; /*如果小于,将a中字符放到数组c中*/ i++; /*i自加*/ } else { c[k]=b[j]; /*如不小于,将b中字符放到c中*/ j++; /*j自加*/ } k++; /*k自加*/ } c[k]='\0'; /*将两个字符串合并到c中后加结束符*/ if(a[i]=='\0') /*判断a中字符是否全都复制到c中*/ p=b+j; /*p指向数组b中未复制到c的位置*/ else p=a+i; /*p指向数组a中未复制到c的位置*/ strcat(c,p); /*将p指向位置开始的字符串连接到c中*/ puts(c); /*将c输出*/ }
举一反三
根据本实例,读者可以:
将已按升序排好的字符串a和字符串b按降序归并到字符串c中并输出。
输入两个字符串,找出字符串中相同的字符并将相同的字符输出。
实例035 在指定的位置后插入字符串
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\035
实例说明
用户先输入两个字符串str1和str2,再输入数值来确定将字符串2插在字符串1的哪个字符后面,最后将插入后的字符串输出。运行结果如图1.35所示。
图1.35 在指定位置后插入字符串
技术要点
本实例中使用了多个字符串处理函数,下面具体介绍strcpy、strncpy及strcat的使用要点。
● strcpy(字符数组1,字符串2)
作用是将字符串2复制到字符数组1中去。
说明如下。
(1)字符数组1的长度不应小于字符串2的长度,应足够的大,以便容纳被复制的字符串2。
(2)“字符数组1”必须写成数组名形式,“字符串2”可以是字符数组名,也可以是一个字符串常量。
(3)复制时连同字符串后面的“\0”一起复制到字符数组1中。
● strncpy(字符数组1,字符串2,size_t maxlen)
作用是复制字符串2中前maxlen个字符到字符数组1中。
● strcat(字符数组1,字符数组2)
作用是连接两个字符数组中的字符串,把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值即字符数组1的地址。
说明如下。
(1)字符数组1必须足够大,以便容纳连接后的新字符串。
(2)连接前两个字符串的后面都有一个“\0”,连接时将字符串1后面的“\0”取消,只在新串最后保留一个“\0”。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h> #include<string.h>
(3)自定义插入函数,实现将字符串2插入到字符串1中的指定位置的后面。在该过程中借助数组string作为临时变量来实现连接。
(4)主函数的编写,首先通过gets函数分别获得str1和str2,然后调用自定义的insert函数,最后将新得到的字符串用puts函数输出。
(5)主要程序代码如下:
char insert(char s[], char t[], int i)/*自定义函数insert*/ { char string[100]; /*定义数组string作为中间变量*/ if (i<0||i>strlen(s)) /*当i超出输入字符串的长度将输出error*/ { printf("error!!\n"); exit(1); } if (!strlen(s)) strcpy(s, t); /*若s数组长度为0,则直接将t数组内容复制到s中*/ else if(strlen(t)) /*若长度不为空,执行以下语句*/ { strncpy(string,s,i); /*将s数组中的前i个字符复制到string中*/ string[i]='\0'; strcat(string,t); /*将t中字符串连接到string*/ strcat(string,(s+i)); /*将s中剩余字符串连接到string*/ strcpy(s,string); /*将string中字符串复制到s中*/ return s; /*返回值为s*/ } } main() { char str1[100],str2[100]; /*定义str1,str2两个字符型数组*/ int position; /*定义变量position为基本整型*/ printf("please input str1:\n"); gets(str1); /*gets函数获得第一个字符串*/ printf("please input str2:\n"); gets(str2); /*gets函数获得第二个字符串*/ printf("please input position:\n"); scanf("%d",&position); /*输入字符串二插入字符串一的位置*/ insert(str1,str2,position); /*调用insert函数*/ puts(str1); /*输出最终得到的字符串*/ }
举一反三
根据本实例,读者可以:
输入一个字符串,删去字母表中k后面的所有字母,将剩下的字母保留并输出在屏幕上。
在任意字符串str中将与字符c相等的所有元素的下标存到数组a中并输出。
1.7 函数
C源程序是由函数组成的,也可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函数式语言。由于采用了函数模块式的结构,C语言易于实现结构化程序设计。使程序的层次结构清晰,便于程序的编写、阅读、调试。
实例036 递归解决年龄问题
这是一个可以提高基础技能的实例
实例位置:光盘\mingrisoft\01\036
实例说明
有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个人,又说比第2人大2岁。问第2个人,说比第1个人大2岁。最后问第1个人,他说是10岁。编写程序当输入第几个人时求出其对应年龄。运行结果如图1.36所示。
图1.36 递归解决年龄问题
技术要点
本实例中age函数被递归调用,这里详细分析下递归调用的过程。
递归的过程分为两个阶段:第一阶段是“回推”,由题可知,要想求第5个人的年龄必须知道第4个人的年龄,要想知道第4个人的年龄必须知道第3个人的年龄……直到第1个人的年龄,这时age(1)的年龄已知,就不用再推。第二阶段是“递推”,从第2个人推出第3个人的年龄……一直推到第5个人的年龄为止。这里要注意必须要有一个结束递归过程的条件,本实例中就是当n=1时f=10,也就是age(1)=10,否则递归过程会无限制进行下去。总之递归就是在调用一个函数的过程中又出现直接或间接地调用该函数本身。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)自定义函数age,用递归调用函数本身的方式来求年龄,代码如下:
int age(int n) /*自定义函数age*/ { int f; if(n= =1) f=10; /*当n等于1时,f等于10*/ else f=age(n-1)+2; /*递归调用age函数*/ return f; /*将f值返回*/ }
(4)主函数编写,输入想要知道的年龄,调用age函数,求出相应的年龄并将其输出。
(5)主要程序代码如下:
main() { int i,j; /*定义变量i,j为基本整型*/ printf("Do you want to know whose age?please input:\n"); scanf("%d",&i); /*输入i的值*/ j=age(i); /*调用函数age求年龄*/ printf("the age is %d",j); /*将求出的年龄输出*/ }
举一反三
根据本实例,读者可以:
输入一个整数,要求用递归的方法分离出这个整数每一位上的数字(例如输入2658,则应该输出2 6 5 8)。
利用递归方法将一个较大的整数分解为若干素数因子的乘积,并打印分解的结果。例如输入36,则应输出3×3×2×2。
实例037 求学生的平均身高
这是一个可以提高基础技能的实例
实例位置:光盘\mingrisoft\01\037
实例说明
输入学生数并逐个输入学生的身高,输出身高的平均值。运行结果如图1.37所示。
图1.37 求学生的平均身高
技术要点
本实例主要采用了数组名作为函数参数,有以下几点需要说明。
(1)用数组名作参数,应该在主调函数和被调用函数中分别定义数组,像本例中主调函数定义的数组为height,被调用函数定义的数组为array。
(2)实参数组与形参数组类型应一致,本实例中都为float。
(3)形参数组也可以不指定大小,在定义数组时在数组名后面跟一个空的方括弧。本实例就没有指定形参数组的大小。
(4)用数组名作函数实参时,不是把数组元素的值传递给形参,而是把实参数组的起始地址传递给形参数组。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)自定义函数average,用for语句实现各个元素值相加,最终求出平均数。
(4)主函数编写,输入学生数及每个学生的身高,调用函数average求出平均数并将结果输出。
(5)主要程序代码如下:
main() { float average(float array[],int n); /*函数声明*/ float height[100],aver; int i,n; printf("please input the number of students:\n"); scanf("%d",&n); /*输入学生数量*/ printf("please input student`s height:\n"); for(i=0;i<n;i++) scanf("%f",&height[i]); /*逐个输入学生的身高*/ printf("\n"); aver=average(height,n); /*调用average函数求出平均身高*/ printf("average height is %6.2f",aver); /*将平均身高输出*/ } float average(float array[],int n) /*自定义求平均身高函数*/ { int i; float aver,sum=0; for(i=0;i<n;i++) sum+=array[i]; /*用for语句实现sum累加求和*/ aver=sum/n; /*总和除以人数求出平均值*/ return(aver); /*返回平均值*/ }
举一反三
根据本实例,读者可以:
自定义函数add(),计算任意实数a与实数b的和并返回。
自定义函数swap(),将数组从第1个元素到第n个元素两两互换。
实例038 分数计算器程序
这是一个可以提高基础技能的实例
实例位置:光盘\mingrisoft\01\038
实例说明
在实际应用中有很多时候我们希望计算机给出的结果是分数而不是小数,本程序就是在这个前提下产生的,具体要求如下:如果用户输入形式是1,2,+,1,3则代表1/2+1/3,要求运算结果以分数形式体现。运行结果如图1.38所示。
图1.38 分数计算器程序
技术要点
本实例需要大家掌握的技术要点是函数的嵌套调用。
C语言规定一个函数内不能包含另一个函数,也就是说不能嵌套定义函数,但是可以在调用一个函数的过程中调用另一个函数,简言之可嵌套调用函数。从本实例来看在执行main函数的过程中,当遇到调用add函数的操作语句时,流程转向add函数,在执行add函数时,当遇到调用gbs函数的操作语句时,流程转向gbs函数,在执行gbs函数时,当遇到调用gys函数的操作语句时,流程转向gys函数,当完成gys函数的全部操作后返回到gbs中,当完成gbs剩下的全部操作后返回到add中执行剩余部分,直到add函数结束,再返回到main函数中,继续执行main剩余部分直到结束(如中间过程中再遇到函数就照上面模式执行)。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)自定义函数gys,求两个数的最大公约数,返回值类型为基本整型,代码如下:
int gys(int x,int y) /*定义求最大公约数函数*/ { return y?gys(y,x%y):x; /*递归调用gys,利用条件语句返回最大公约数*/ }
(4)自定义函数gbs,求两个数的最小公倍数,返回值类型为基本整型,代码如下:
int gbs(int x,int y) /*定义求最小公倍数函数*/ { return x/gys(x,y)*y; }
(5)自定义函数yuefen,实现两个数约分,此函数不带回值,代码如下:
void yuefen(int fz,int fm) /*定义约分函数*/ { int s=gys(fz,fm); fz/=s; fm/=s; printf("the result is %d/%d\n",fz,fm); }
(6)自定义函数add、sub、mul、div,实现分数的加法、减法、乘法、除法运算。这4个函数均不带回值,代码如下:
void add(int a,int b,int c,int d) /*定义加法函数*/ { int u1,u2,v=gbs(b,d),fz1,fm1; u1=v/b*a; u2=v/d*c; fz1=u1+u2; fm1=v; yuefen(fz1,fm1); } void mul(int a,int b,int c,int d) /*定义乘法函数*/ { int u1,u2; u1=a*c; u2=b*d; yuefen(u1,u2); } void sub(int a,int b,int c,int d) /*定义减法函数*/ { int u1,u2,v=gbs(b,d),fz1,fm1; u1=v/b*a; u2=v/d*c; fz1=u1-u2; fm1=v; yuefen(fz1,fm1); } void div(int a,int b,int c,int d) /*定义除法函数*/ { int u1,u2; u1=a*d; u2=b*c; yuefen(u1,u2); }
(7)主函数编写,按要求输入要运算的内容,使用switch语句,根据输入的运算符号决定调用哪个函数。
(8)主要程序代码如下:
main() { char op; int a,b,c,d; scanf("%ld,%ld,%c,%ld,%ld",&a,&b,&op,&c,&d); switch(op) /*根据输入的符号选择不同函数的调用*/ { case'+':add(a,b,c,d);break; /*调用加法函数*/ case'*':mul(a,b,c,d);break; /*调用乘法函数*/ case'-':sub(a,b,c,d);break; /*调用减法函数*/ case'/':div(a,b,c,d);break; /*调用除法函数*/ } }
举一反三
根据本实例,读者可以:
编程实现简单的计算器功能。
编写函数month,输出2010年日历。
1.8 趣味计算
编写程序的过程往往也是一种体验乐趣的过程,可以通过编写C语言程序解决一些通过笔算显得有些复杂的趣味小计算,本节就介绍了几个这样的实例。
实例039 加油站加油
本实例是一个提高基础技能的程序
实例位置:光盘\mingrisoft\01\039
实例说明
某加油站有a、b、c 3种汽油,售价分别为3.25、3.00、2.75(元/千克),也提供了“自己加”或“协助加”两个服务等级,这样用户可以得到5%或10%的优惠。编程实现针对用户输入加油量x,汽油的品种y和服务的类型z,输出用户应付的金额。运行结果如图1.39所示。
图1.39 加油站加油
技术要点
本程序通过switch循环来实现不同的选择,switch语句的应用前面有介绍,这里大家可以看下前面的例子复习一下。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)从键盘中输入油的千克数、种类及服务,通过switch给变量赋相应的值,计算出应付的钱数。
(4)主函数程序代码如下:
main() { float x, m1, m2, m; char y, z; scanf("%f,%c,%c",&x,&y,&z); /*输入选择油的千克数、种类及服务*/ switch(y) { case 'a': m1 = 3.25; break; case 'b': m1 = 3.00; break; case 'c': m1 = 2.75; break; } switch(z) { case 'a': m2 = 0; break; case 'm': m2 = 0.05; break; case 'e': m2 = 0.1; break; } m=x*m1-x*m1*m2; /*计算应付的钱数*/ printf("the type of oil is:%c\n", y); printf("the type of server is:%c\n", z); printf("the money is:%.3f", m); }
举一反三
根据本实例,读者可以:
从输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续判断第二个字母。
50米赛跑测试时间小于8秒的同学用A表示,在8秒~9.5秒之间的用B表示,9.5秒以上的用C表示。
实例040 小球下落
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\040
实例说明
一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?第10次反弹多高?运行结果如图1.40所示。
图1.40 分数计算器程序
技术要点
想解决本题最主要是要分析小球每次弹起的高度与落地次数之间的关系。这里给大家分析一下:小球从100高处自由下落当第一次落地时经过了100米,这个可单独考虑,从第一次弹起到到第二次落地前经过的路程为前一次弹起最高高度的一半乘以2加上前面经过的路程,因为每次都有弹起和下落两个过程其经过的路程相等故乘以2。以后的几次依此类推,那么到第十次落地前共经过了九次这样的过程,所以程序中for循环执行循环体的次数是九次。题目中还提到了第十次反弹的高度,这个我们只需在输出时用第九次弹起的高度除以2即可。
当读者以后做程序遇到此类问题时不要急着先写程序,应先理清题目思路找出其中规律,这时再写程序会更加得心应手。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)定义i,h,s为单精度型并为h、s赋初值100。
(4)使用for语句计算每一次弹起到落地所经过的路程与前面经过路程和的累加。
(5)将经过的总路程及第十次弹起的高度输出。
(6)主要程序代码如下:
main() { float i,h=100,s=100; /*定义变量i,h,s分别为单精度型并为h和s赋初值100*/ for(i=1;i<=9;i++) /*for语句,i的范围从1到9表示小球从第二次落地到第十次落地*/ { h=h/2; /*每落地一次弹起高度变为原来一半*/ s+=h*2; /*累积的高度和加上下一次落地后弹起与下落的高度*/ } printf("the total length is %f\n",s); /*将高度和输出*/ printf("the height of tenth time is %f",h/2);/*输出第十次落地后弹起的高度*/ }
举一反三
根据本实例,读者可以编程实现以下问题:
某人有5张2角的邮票,3张5角的邮票,问用这些邮票中的一张或几张能得到多少种不同的邮资。
从键盘上输入两整数n、m,表示n只猴子顺时针围成一圈,从第一只猴子开始顺时针数到第m只猴子,令其离开队伍,然后继续数到第m只猴子,再令其离开队伍,直到最后剩下的一只猴子为大王,请输出大王的编号。
实例041 灯塔数量
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\041
实例说明
有一八层灯塔,每层的灯数都是上一层的一倍,共有765盏灯,编程求最上层与最下层的灯数。运行结果如图1.41所示。
图1.41 灯塔数量
技术要点
本实例没有太多难点,通过对n的穷举,探测满足条件条件的n值。在计算灯的总数时我们先计算2楼到8楼灯的总数,再将计算出的和加上1楼灯的数量,这样就求出了总数,当然我们也可以将一楼灯的数量赋给sum之后再加上2楼到8楼灯的数量。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include<stdio.h>
(3)利用while循环对n的值从1开始进行穷举,在计算2楼到8楼灯的数目时程序中用到了for循环。
(4)主函数程序代码如下:
main() { int n=1,m,sum,i; /*定义变量为基本整形*/ while(1) { m=n; /*m存储一楼灯的数量*/ sum = 0; for(i = 1; i < 8; i++) { m=m*2; /*每层楼灯的数量是上一层的2倍*/ sum+=m; /*计算出除一楼外灯的总数*/ } sum+=n; /*加上一楼灯的数量*/ if(sum==765) /*判断灯的总数量是否达到765*/ { printf("the first floor has %d\n",n);/*输出一楼灯的数量*/ printf("the eight floor has %d",m); /*输出八楼灯的数量*/ break; /*跳出循环*/ } n++; /*灯的数量加1,继续下次循环*/ } }
举一反三
根据本实例,读者可以编程实现以下问题:
有一个十层的架子,第一层放了多少块糖果并不知道,但是每层放的糖果数都是前一层的2倍多一块,第十层放了4607块糖果,问第一层放了多少块糖果。
5位跳水高手参加10米高台跳水决赛,有好事者让5人据实力预测比赛结果。A选手说:B第二,我第三;B选手说:我第二,E第四;C选手说:我第一,D第二;D选手说:C最后,我第三;E选手说:我第四,A第一.决赛成绩公布之后,每位选手的预测都只说对了一半,即一对一错,请编程解出比赛的实际名次。
实例042 买苹果
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\042
实例说明
每个苹果0.8元,第一天买2个苹果,第二天开始每天买前一天的2倍,直到购买的苹果个数达到不超过100的最大值,编程求每天平均花多少钱?运行结果如图1.42所示。
图1.42 买苹果问题
技术要点
解决本实例首先来分析下题目要求,假设每天购买的苹果数为n,花的钱数总和为money,那么money和n之间的关系这里我们可以通过一个等式来说明,即money=money+0.8*n,它的具体含义是截止到目前所花的钱数等于今天所购买的苹果花的钱数与之前所花的钱数的总和。这里大家应注意 n 的变化,n 初值应为2,随着天数每天增加(day++),n值随之变化即n=n*2,以上过程应在while循环体中进行,那么什么才是这个while语句结束的条件呢?根据题意可知“购买的苹果个数应是不超过100的最大值”,那么很明显n的值是否小于100便是判断这个while语句是否执行的条件。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)定义变量n,day为基本整型并赋初值分别为2和0,定义变量money,ave为单精度型,并给money赋初值为0。
(4)使用while语句实现每天所买苹果钱数的累加和天数自加以及每天所买苹果数的变化。
(5)求出平均数并将其输出。
(6)主要程序代码如下:
main() { int n=2,day=0; /*定义n,day为基本整型*/ float money=0,ave; /*定义money,ave为单精度型*/ while(n<100) /*苹果个数不超过100,故while中表达式n小于100*/ { money+=0.8*n; /*将每天花的钱数累加求和*/ day++; /*天数自加*/ n*=2; /*每天买前一天个数的2倍*/ } ave=money/day; /*求出平均每天花的钱数*/ printf("The result is %.6f",ave); /*将求出的ave输出*/ }
举一反三
根据本实例,读者可以:
从键盘中输入任意一个数,要求输出包括该数在内的10个数据,这10个数据每一个数都是前一个数的2倍减1。
从键盘中输入任意两个数,要求输出包括这两个数在内的10个数据,除前两个外的剩下8个数据每一个数都是前两个数的2倍减1。
实例043 猴子吃桃
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\043
实例说明
猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个,第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。编写程序求第一天共摘了多少。运行结果如图1.43所示。
图1.43 猴子吃桃
技术要点
本实例和前两个实例解题的思路基本上是一样的。就是先找出变量间的关系,找出了它们之间的关系后程序就基本上没有什么问题了。本题中读者要明确第一天桃子数和第二天桃子数之间的关系即第二天桃子数加1的2倍等于第一天的桃子数。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)定义day、x1、x2为基本整型,并为day,x2赋初值9和1。
(4)使用while语句从后向前推出第一天摘的桃子数。
(5)将最终求出的结果输出。
(6)主要程序代码如下:
main() { int day,x1,x2; /*定义day,x1,x2三个变量为基本整型*/ day=9; x2=1; while(day>0) { x1=(x2+1)*2; /*第一天的桃子数是第二天桃子数加1后的2倍*/ x2=x1; day--; /*因为从后向前推天数递减*/ } printf("the total is %d\n",x1); /*输出桃子的总数*/ }
举一反三
根据本实例,读者可以编程实现以下问题:
15个教徒和15个非教徒在海上遇险,必须将一半的人投入海中,其余的人才能幸免遇难,于是想了一个办法:30个人围成一个圆圈,从第一个人开始依次报数,每数到第9个人就将他扔入大海,如此循环进行直到剩下15个人为止,问怎样排才能使每次投入大海的都是非教徒。
两个乒乓球队进行比赛,各出3人。甲队为a、b、c 3人,乙队为x、y、z 3人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x、z比,请编程序找出3队赛手的名单。
实例044 老师分糖果
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\044
实例说明
幼儿园老师将糖果分成了若干等份,让学生按任意次序上来领,第1个来领的,得到1份加上剩余糖果的十分之一;第2个来领的,得到2份加上剩余糖果的十分之一;第3个来领的,得到3份加上剩余糖果的十分之一,……依此类推。问共有多少个学生,老师共将糖果分成了多少等份?运行结果如图1.44所示。
图1.44 老师分糖果
技术要点
本实例读者在刚看时也许会感觉无从下手,这里就给大家介绍1个方法即采用穷举法进行探测,由部分推出整体。设老师共将糖果分成n等份,第1个学生得到的份数为sum1=(n+9)/10,第2个学生得到的份数为sum2=(9*n+171)/100,为n赋初值,本实例中n初值赋11(糖果份数至少为11份时,第一个来领的同学领到的才是完整的份数),穷举法直到sum1=sum2.,这样就可以计算出老师将糖果分成了多少份和学生的数量。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)定义n为基本整型,sum1和sum2为单精度型。
(4)使用穷举法,这里用for语句对n逐个判断,直到满足条件sum1=sum2,结束for循环。这里有一个问题值得大家注意,因为将糖果分成了n等份,所以最终求出的结果必须是整数。
(5)将最终求出的结果输出,这里注意一下学生数量是用总份数除以每个人得到的份数,程序里是除以第一个人得到的份数。
(6)主要程序代码如下:
main() { int n; float sum1,sum2; /*sum1和sum2应为单精度型,否则结果将不准确*/ for(n=11;;n++) { sum1=(n+9)/10.0; sum2=(9*n+171)/100.0; if(sum1!=(int)sum1)continue; /*sum1和sum2应为整数,否则结束本次循环继续下次判断*/ if(sum2!=(int)sum2)continue; if(sum1==sum2)break; /*当sum1等于sum2时,跳出循环*/ } printf("\n the number of students is %d\n gong fen le %d fen",(int)(n/sum1),n); /*输出学生数及分成的份数*/ }
举一反三
根据本实例,读者可以编程实现以下问题:
已知5个互不相同的正整数之和为23,且从这5个数中挑选若干个加起来可以表示从1到23之内的全部自然数,问这5个数都是什么。
有这样5个数,将这5个数的每位分离出来正好表示从1~9这9个数字且不重复,如果将这5个数按一定次序排,那么后一个数字将比前一个数大9,求这5个数。
实例045 新同学的年龄
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\045
实例说明
班里来了一名新同学,很喜欢学数学,同学们问他年龄的时候,他和大家说:“我的年龄的平方是个三位数,立方是个四位数,四次方是个六位数。三次方和四次方正好用遍0、1、2、3、4、5、6、7、8、9这十个数字,那么大家猜猜我今年多大?”,运行结果如图1.45所示。
图1.45 新同学年龄
技术要点
首先考虑年龄的范围,因为17的四次方是83521,小于六位,22的三次方是10648,大于4位,所以年龄的范围就确定出来即大于等于18小于等于21。其次是对18到21之间的数进行穷举的时候应将算出的四位数和六位数的每位数字分别存于数组中,再对这10 个数字进行判断,看有无重复或是否有数字未出现,这些方面读者在编写程序的时候都要考虑全面。最后将运算出的结果输出即可。
本实例的关键技术还是在于对数组的灵活应用,即如何将四位数及六位数的每一位存入数组中并对存入的数据怎样做无重复的判断。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)定义数组及变量为长整形,这主要是由于变量n4用来存储6位数。
(4)do-while循环对18到21中的所用数进行判断,用“%”“/”的方法将四位数及六位数的各位数字分别存到数组中,利用数组s对各个数字在数组中出现的次数做统计,凡出现不是一次的均不是符合要求的数,对数组中的十个数字全部判断后将符合要求的数输出。
(5)主要程序代码如下:
main() { long a[10]={0},s[10]={0},i,n3,n4,x=18; /*因为有六位数出现,所以定义为长整形*/ do { n3=x*x*x; /*求出x的三次方*/ for(i=3;i>=0;i--) { a[i]=n3%10; /*取这个三位数的各位数字*/ n3/=10; } n4=x*x*x*x; /*求x的四次方*/ for(i=9;i>=4;i--) { a[i]=n4%10; /*取这个四位数的各位数字*/ n4/=10; } for(i=0;i<=9;i++) s[a[i]]++; /*统计数字出现次数*/ for(i=0;i<=9;i++) if(s[i]==1) /*判断有无重复数字*/ { if(i= =9) printf("\n the number is %ld\n",x); /*将结果输出*/ } else break; /*跳出for循环*/ x++; }while(x<22); /*x的最大值取到21*/ }
举一反三
根据本实例,读者可以编程实现以下问题:
有这样3个数,将这3个数的每位分离出来正好表示从1~9这9个数字且不重复,如果将这3个数按一定次序排,那么后一个数字将比前一个数大333,求这3个数。
小明新买了一本习题册,如果每天写n页,6天后还有1页就完成本习题册,如果每天再多写1页,4天后还有3页就完成本习题册,编程求该习题册有多少页?
实例046 百钱百鸡
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\046
实例说明
中国古代数学家张丘建在他的《算经》中提出了一个著名的“百钱买百鸡问题”,鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁、母、雏各几何?运行结果如图1.46所示。
图1.46 百钱百鸡问题
技术要点
根据题意设公鸡、母鸡和雏鸡分别为cock、hen和chick,如果100元全买公鸡那么最多能买20只,所以cock的范围是大于等于0小于等于20,如果全买母鸡那么最多能买33只,所以hen的范围是大于等于零小于等于33,如果100元钱全买小鸡那么根据题意最多能买99只(根据题意小鸡的数量应小于100且是3的倍数)。在确定了各种鸡的范围后进行穷举并判断,判断的条件有以下3点。
● 所买的3种鸡的钱数总和为100。
● 所买的3种鸡的数量之和为100。
● 所买的小鸡数必须是3的倍数。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)使用for语句对3种鸡的数量在事先确定好的范围内进行穷举并判断,对满足条件的将3种鸡的数量按指定格式输出,否则进行下次循环。
(4)主要程序代码如下:
main() { int cock,hen,chick; /*定义变量为基本整型*/ for(cock=0;cock<=20;cock++) /*公鸡范围在0到20之间*/ for(hen=0;hen<=33;hen++) /*母鸡范围在0到33之间*/ for(chick=3;chick<=99;chick++) /*小鸡范围在3到99之间*/ if(5*cock+3*hen+chick/3==100) /*判断钱数是否等于100*/ if(cock+hen+chick==100) /*判断购买的鸡数是否等于100*/ if(chick % 3==0) /*判断小鸡数是否能被3整除*/ printf("cock:%d hen:%d chick:%d\n", cock, hen,chick); }
举一反三
根据本实例,读者可以编程实现以下问题:
围绕一个山顶有10个洞,一只兔子和一只狐狸各居一洞,狐狸第一次隔一个洞找,第二次隔两个洞找,依此类推,假使狐狸找1000次,问哪个洞是安全的。
选择不同日期,限制不同时间,实现定时开关机。
实例047 彩球
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\047
实例说明
在一个袋子里装有三色彩球,其中红色球有3个,白色球也有3个,黑色球有6个,问当从袋子中取出8个球时共有多少种可能的方案。编程实现将所有可能的方案编号输出在屏幕上。运行结果如图1.47所示。
图1.47 彩球问题
技术要点
本实例和百钱百鸡问题在解题思路上基本相同,都是要先确定范围,本实例要确定各种颜色球的范围,红球和白球的范围根据题意可知均是大于等于0小于等于3,不同的是本实例将黑球的范围作为if语句中的判断条件,即用要取出的球的总数目8减去红球及白球的数目所得的差应小于黑球的总数目6。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)定义i、j、count分别为基本整型,count赋初值为1,这里起到计数的功能。
(4)使用for语句进行穷举,对满足if语句中条件的可能方案按指定格式进行输出,否则进行下次循环。
(5)主要程序代码如下:
main() { int i, j, count; puts("the result is:\n"); printf("time red ball white ball black ball\n"); count = 1; for(i=0;i<=3;i++) /*红球数量范围0到3之间*/ for(j=0;j<=3;j++) /*白球的数量范围0到3之间*/ if((8-i-j)<=6) /*判断要取黑色球的数量是否在6个以内*/ printf("%3d%8d%9d%10d\n",count++,i,j,8-i-j); /*输出各种颜色球的数量*/ return 0; }
举一反三
根据本实例,读者可以:
输入n后再输入n个正整数,求n个数的最大公约数。
编程求输入的任意3个整数的最小公倍数。
实例048 求总数
这是一个可以启发思维的实例
实例位置:光盘\mingrisoft\01\048
实例说明
集邮爱好者把所有的邮票存放在3个集邮册中,在A册内存放全部的十分之二,在B册内存放全部的七分之几,在C册内存放303张邮票,问这位集邮爱好者集邮总数是多少,以及每册中各有多少邮票?运行结果如图1.48所示。
图1.48 求总数问题
技术要点
根据题意可设邮票总数为sum,B册内存放全部的x/7,则可列出:
sum=2*sum/10+x*sum/7+303
经化简可得sum=10605/(28-5*x);从化简的等式来看我们可以确定出x的取值范围是从1到5,还有一点我们要明确就是邮票的数量一定是整数不可能出现小数或其他,这就要求x必须要满足10650%(28-5*x)==0。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件:
#include <stdio.h>
(3)定义a,b,c,x及sum分别为基本整型。
(4)对x的值进行试探,满足10650%(28-5*x)==0的x值即为所求,通过此值计算出邮票总数及各个集邮册中邮票的数量。
(5)主要程序代码如下:
main() { int a, b, c, x, sum; for(x=1;x<=5;x++) /*x的取值范围从1到5*/ { if(10605 %(28-5*x)==0) /*满足条件的x值即为所求*/ { sum=10605/(28-5*x); /*计算出邮票总数*/ a=2*sum/10; /*计算a集邮册中的邮票数*/ b=5*sum/7; /*计算b集邮册中的邮票数*/ c=303; /*c集邮册中的有票数*/ printf("total is %d\n",sum); /*输出邮票的总数*/ printf("A:%d\n",a); /*输出A集邮册中的邮票数*/ printf("B:%d\n",b); /*输出B集邮册中的邮票数*/ printf("C:%d\n",c); /*输出C集邮册中的邮票数*/ } } }
举一反三
根据本实例,读者可以:
从自然数1、2……n中任取r个数的所有组合。
输入出生年月日,计算出生到今天的天数。
1.9 宏定义与位运算
在前面各章中,已多次使用过以“#”号开头的预处理命令,如命令#include,宏定义命令#define等。在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们称为预处理部分。本节就介绍一个宏定义的实例。
实例049 用宏定义实现值互换
这是一个可以提高基础技能的实例
实例位置:光盘\mingrisoft\01\049
实例说明
试定义一个带参数的宏swap(a,b),以实现两个整数之间的交换,并利用它将一维数组a和b的值进行交换。运行结果如图1.49所示。
图1.49 用宏定义实现值互换
技术要点
本实例关键技术是要掌握带参数的宏定义的一般形式及使用时的注意事项,具体如下。
一般形式为:
#define宏名(参数表)字符串
说明如下。
(1)对带参数的宏的展开只是将语句中的宏名后面括号内的实参字符串代替#define命令行中的形参。
(2)在宏定义时,在宏名与带参数的括号之间不可以加空格,否则将空格以后的字符都作为替代字符串的一部分。
(3)在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件stdio.h:
#include <stdio.h>
(3)进行带参数的宏swap(a,b)的定义:
#define swap(a,b){int c;c=a;a=b;b=c;} /*定义一个带参的宏swap*/
(4)主要程序代码如下:
main() { int i,j,a[10],b[10]; /*定义数组及变量为基本整型*/ printf("please input array a:\n"); for(i = 0; i < 10; i++) scanf("%d",&a[i]); /*输入一组数据存到数组a中*/ printf("please input array b:\n"); for(j = 0; j < 10; j++) scanf("%d",&b[j]); /*输入一组数据存到数组b中*/ printf("the array a is:\n"); for(i = 0; i < 10; i++) printf("%d,",a[i]); /*输出数组a中的内容*/ printf("the array b is:\n"); for(j = 0; j < 10; j++) printf("%d,",b[j]); /*输出数组b中的内容*/ for(i = 0; i < 10; i++) swap(a[i],b[i]); /*实现数组a与数组b对应值互换*/ printf("Now the array a is:\n"); for(i = 0; i < 10; i++) printf("%d,",a[i]); /*输出互换后数组a中的内容*/ printf("Now the array b is:\n"); for(j = 0; j < 10; j++) printf("%d,",b[j]); /*输出互换后数组b中的内容*/ }
举一反三
根据本实例,读者可以:
输入两个整数,求它们相加的和,用带参的宏实现。
输入两个整数,求它们相减的差,用带参的宏实现。
实例050 循环移位
这是一个可以提高基础技能的实例
实例位置:光盘\mingrisoft\01\050
实例说明
编程实现循环移位,具体要求如下:首先从键盘中输入一个八进制数,其次再输入要移位的位数(当为正数时表示向右循环移位,否则表示向左循环移位),最后将移位的结果显示在屏幕上。运行结果如图1.50所示。
图1.50 循环移位
技术要点
本实例的重点是了解循环移位的具体过程(这里以向右循环移位为例)。
(1)如上图所示,将x的右端n位先放到z中的高n位中。由以下语句实现:
z=x<<(16-n);
(2)将x右移n位,其左面高位n位补0。由以下语句实现:
y=x>>n;
(3)将y与z进行按位或运算。由以下语句实现:
c=a>>n;
实现过程
(1)在TC中创建一个C文件。
(2)引用头文件stdio.h:
#include <stdio.h>
(3)自定义right()函数,用来实现循环右移,程序代码如下:
right(unsigned value,int n) /*自定义循环右移函数*/ { unsigned z; z=(value>>n)|(value<<(16-n)); /*循环右移的实现过程*/ return(z); }
(4)自定义left()函数,用来实现循环左移,程序代码如下:
left(unsigned value,int n) /*自定义左移函数*/ { unsigned z; z=(value>>(16-n))|(value<<n); /*循环左移的实现过程*/ return z; }
(5)主要程序代码如下:
main() { unsigned a; int n; printf("please input a number:\n"); scanf("%o",&a); /*输入一个八进制数*/ printf("please input the number of displacement:\n"); scanf("%d",&n); /*输入要移位的位数*/ if(n > 0) { right(a,n); /*调用自定义的右移函数*/ printf("the result is:%o\n",right(a,n)); /*将右移后的结果输出*/ } else { n= -n; /*将n转为正值*/ left(a,n); /*调用自定义的左移函数*/ printf("the result is %o:\n",left(a,n)); /*将左移后的结果输出*/ } }
举一反三
根据本实例,读者可以:
编程实现输入一个十进制数,同时输入向右移位的位数,以二进制形式输出移位后的结果。
编程实现输入一个十六进制数,同时输入向左移位的位数,以二进制形式输出移位后的结果。