前言
本文章是一个大合集,按照课后习题的命名方式命名,方便寻找,只需要在目录上点相对应的题号即可在这里插入图片描述
第一章课后习题
1.1 编写一个C程序
题目概述:
请参照本章例题,编写一个C程序,输出一下信息:
*****************************
Very good!
*****************************
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
printf("*****************************\n");
printf(" Very good! \n");
printf("*****************************\n");
return 0;
}
1.2 输出其中最大者
题目概述:
编写一个C程序,输入a,b,c三个值,输出其中最大者
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
int max = a;
if (max < b)
max = b;
if (max < c)
max = c;
printf("%d", max);
return 0;
}
1.3 上机运行三章例题
题目概述:
要求在屏幕上显示出以下一行信息
This is a C program.
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
printf("This is a C program.\n");
return 0;
}
题目概述:
求两个整数之和
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int a, b, sum;
a = 123;
b = 456;
sum = a + b;
printf("%d", sum);
return 0;
}
题目概述:
求两个整数中的大者
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int max(int x, int y);
int a, b;
scanf("%d %d", &a, &b);
int c = max(a, b);
printf("%d", c);
return 0;
}
int max(int x, int y)
{
int z = 0;
if (x > y)
z = x;
else
z = y;
return z;
}
第二章课后习题
2.1 计算国民生产总值增长
题目概述:
假如我国国民生产总值的年增长率为10%,计算10年后我国国民生产总值与现在相比增长多少百分比
计算公式为:
r为年增长率,n为年数,P为与现在相比的百分比
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
int main()
{
double r = 0.1;
double P;
int n = 10;
P = pow((1 + 0.1), 10);
printf("%lf", P);
return 0;
}
2.2 贷款利息计算
题目概述:
存款利息的计算。有 1000 元,想存 5 年,可按以下 5 种办法存:
(1)一次存 5年期。
(2) 先存 2 年期,到期后将本息再存 3 年期
(3) 先存 3 年期,到期后将本息再存 2 年期
(4) 存 1 年期,到期后将本息存再存 1年期,连续存 5 次。
(5) 存活期存款。活期利息每一季度结算一次。
某年银行存款利息如下:
1 年期定期存款利息为 4.14%。
2 年期定期存款利息为 4.68%。
3 年期定期存款利息为 5.4%。
5 年期定期存款利息为 5.85%。
活期存款利息为 0.72%(活期存款每一季度结算一次利息)。
如果 r 为年利率,n 为存款年数,则计算本息和的公式为:
一年定期本息和:
n年定期本息和:
存n次一年期的本息和:
说明:
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
int main()
{
int p = 1000; //本金
int n = 5; //存款年限
float r0 = 0.0072; //活期存款
float r1 = 0.0414; //1年期年利率
float r2 = 0.0468; //2年期年利率
float r3 = 0.0540; //3年期年利率
float r5 = 0.0585; //5年期年利率
float p1 = 1000 * (1 + n * r5); //1次存5年期间
float p2 = 1000 * (1 + 2 * r2) * (1 + 3 * r3); //先存2年期,到期后将本息再存3年期
float p3 = 1000 * (1 + 3 * r3) * (1 + 2 * r2); //先存3年期,到期后将本息再存2年期
float p4 = 1000 * pow(1 + r1, n); //存1年期,到期后将本息存再存1年期,连续存5次
float p5 = 1000 * pow(1 + r0 / 4, 4 * n); //存活期存款。活期利息每一季度结算一次
printf("%.2f\n", p1);
printf("%.2f\n", p2);
printf("%.2f\n", p3);
printf("%.2f\n", p4);
printf("%.2f\n", p5);
return 0;
}
2.3 China译成密码
题目概述:
请编程序将 China 译成密码,密码规律是:用原来的字母后面第 4 个字母代替原来的字母。例如,字母 A后面第 4 个字母是 E,用E代替 A。因此,China 应译为 GImre。请编写程序,用赋初值的方法使 cl,c2,c3,c4,c5 这 5 个变量的值分别为‘C’、‘h’、’i、‘n’、’ a’,经过运算,使 c1,c2,c3,c4,c5 分别变为G、I、m、r、e,并输出。
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
int main()
{
char c1 = 'C';
char c2 = 'h';
char c3 = 'i';
char c4 = 'n';
char c5 = 'a';
c1 += 4;
c2 += 4;
c3 += 4;
c4 += 4;
c5 += 4;
printf("password is : %c%c%c%c%c", c1, c2, c3, c4, c5);
return 0;
}
延伸:
2.3 中能否改成?
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int c1, c2; //原为 char c1,c2,今把 cl,c2 改为 4 字节的整型变量
c1 = 97;
c2 = 98;
printf("%c %c\n", c1, c2);
printf("%d %d\n", c1, c2);
return 0;
}
(1)运行时会输出什么信息?为什么?
(2)如果将程序6、7行改为
c1 = 197;
c2 = 198;
运行时会输出什么信息?为什么?
第三章课后习题
3.1 表达式的简答题
-
怎样区分表达式和表达式语句? 可以参考教材P64 (3)表达式语句
- 书本说法:表达式语句由一个表达式+一个分号构成,也就是一个表达式 + 分号就是表达式语句
- a = 3 表达式
- a = 3; 表达式语句
- 而表达式是一个值,表达式语句是一条指令(参考书本P63中C语言的语句用来向计算机系统发出操作指令)
- 书本说法:表达式语句由一个表达式+一个分号构成,也就是一个表达式 + 分号就是表达式语句
-
C 语言为什么要设表达式语句?
- 原因:用短的指令代替了复杂的函数运算,程序运行效率高。
-
什么时候用表达式?
-
当我们需要将表达式的值用于某些操作时,我们应该使用表达式
-
最简单的例子,就是用表达式的值进行语句的判断
int x = 3; if(x > 3) ;
-
-
-
什么时候用表达式语句?
- 我们需要表达式的副作用,而不是它的值
- 最简单的例子, a++;
- 我们需要表达式的副作用,而不是它的值
3.2 输入输出函数简答题
C语言为什么要把输入输出的功能作为函数,而不作为语言的基本部分?
- 将输入输出视为函数而不是语言的基本部分,使程序员更容易编写可重复使用的模块化代码。将输入输出作为函数,程序员可以使用不同输入源和输出目的地,而不必更改他们的代码。
- 将输入输出作为函数还有助于提高代码的可读性和可维护性,函数名和参数列表可以清晰地表达函数的目的和行为,使代码更易于理解和调试。
- 将输入输出作为函数还可以帮助编译器进行优化。将输输出转换为函数调用,使编译器可以在优化时更容易将功能拆分为单独的模块,并更好地利用系统资源。
3.3 scanf输入数据问题
题目概述:
用下面的scaf函数输入数据,使a=3,b=7,x=8.5,y=71.82,c1=‘A’,c2=‘a’。如何在键盘上输入
#include<stdio.h>
int main()
{
int a, b;
float x, y;
char c1, c2;
scanf("a = %d, b = %d", &a, &b);
scanf("%f %e", &x, &y);
scanf("%c %c", &c1, &c2);
return 0;
}
输入如下:
注意
-
书本中的第七行是没有那么多空格的,是
a=%d b = %d
,在输入时要严格按照这个格式进行输入,否则无法输出 -
其次,在输入完71.82后,我们发现,其实%e和%c是紧挨着的,是不能加空格的,如果加了空格,空格是字符之一,那么scanf就会读入空格并赋给c1,看下方:
-
如果你发现在你的程序敲出了上述代码后,运行发现是空的,那是因为你没有加上输出函数,加上一个printf函数即可
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a, b; float x, y; char c1, c2; scanf("a = %d, b = %d", &a, &b); scanf("%f %e", &x, &y); scanf("%c %c", &c1, &c2); printf("输出为:\n"); printf("a = %d, b = %d\n%f %e\n%c %c", a, b, x, y, c1, c2); return 0; }
3.4 scanf输入数据问题(2)
题目概述:
下面的scanf函数,输入数据,使a=10,b=20,c1=‘A‘,c2=‘a‘,x=1.5,y=-3.75,z=67.8,请问如何在键盘上输入数据
scanf("%5d%5d%c%c%f%f%*f,%f", &a, &b, &c1, &c2, &x, &y, &z);
输出如下:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
注意事项:
- 首先是%5d的输入,他是占5列,所以打3个空格输入10 再加3空格输入20
- 其次就是字符的输入,字符是不能打空格的,因为空格算是字符,字符会被scanf读入并赋值给A1,详情请看3.3的解释
- 还有就是后面的实数就可以打空格了,因为scanf函数除了读%c(字符)的时候会读入,在读入整数%d,实数%f的时候是跳过空格不读的,所以我们在输入的时候可以放心的加入空格
- 最后是%*f,这个意思是:禁止赋值,所以我们在输入时,可以随便输入一个实数,他是不会赋值给任何一个变量的
- 再注意就是记得最后的字符之前是有逗号的,不要忘记加了
3.5 圆的一生
题目概述:
设圆半径r=1.5,圆柱高 h=3,求圆周长、圆面积、圆球表面积、圆球体积、圆柱体积。
用 scanf输人数据,输出计算结果,输出时要求有文字说明,取小数点后 2 位数字。请编程序。
野鸡有话说:
这道题要想写对,首先你要知道圆上面的公式,整理如下:
圆的周长:
圆的面积:
圆球表面积:
圆球体积:
圆柱体积:
知道了上述公式,编写程序手到擒来
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
float r = 1.5;
float h = 3;
float pi = 3.1415926;
float c = 0;//圆周长
float s = 0;//圆面积
float sq = 0;//圆球表面积
float vq = 0;//圆球体积
float vz = 0;//圆柱体积
scanf("%f %f", &r, &h);
c = 2 * pi * r;
s = pi * r * r;
sq = 4 * pi * r * r;
vq = 4 / 3 * pi * r * r * r;
vz = pi * r * r * h;
printf("圆周长为: %.2f\n", c);
printf("圆面积为: %.2f\n", s);
printf("圆球表面积为: %.2f\n", sq);
printf("圆球体积为: %.2f\n", vq);
printf("圆柱体积为: %.2f\n", vz);
return 0;
}
知道了上述公式,编写程序手到擒来
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
float r = 1.5;
float h = 3;
float pi = 3.1415926;
float c = 0;//圆周长
float s = 0;//圆面积
float sq = 0;//圆球表面积
float vq = 0;//圆球体积
float vz = 0;//圆柱体积
scanf("%f %f", &r, &h);
c = 2 * pi * r;
s = pi * r * r;
sq = 4 * pi * r * r;
vq = 4 / 3 * pi * r * r * r;
vz = pi * r * r * h;
printf("圆周长为: %.2f\n", c);
printf("圆面积为: %.2f\n", s);
printf("圆球表面积为: %.2f\n", sq);
printf("圆球体积为: %.2f\n", vq);
printf("圆柱体积为: %.2f\n", vz);
return 0;
}
运行结果:
3.6 华氏度转摄氏度
题目概述:
输入一个华氏温度,要求输出摄氏温度。公式为:
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
float f = 0;
float c = 0;
printf("请输入一个华氏温度:");
scanf("%f", &f);
c = 5.0 / 9 * (f - 32);
printf("摄氏度为:%.2f°C\n", c);
return 0;
}
- 在计算摄氏度的时候,也就是11行,要注意把一个数写成实型,否则5/9两个都是整型,相除为0,就会出现输出是0的情况
3.7 putchar和printf
题目概述:
用getchar函数读入两个字符给c1,c2,分别用putchar和printf输出这两个字符。思考以下问题:
(1)变量c1和c2定义为字符型还是整型?或二者皆可?
(2)要求输出c1和c2的ASCII码,应如何处理?
(3)整形变量和字符变量是否在任何情况下都可以互相代替?char c1, c2;和int c1, c2;是否无条件等价?
野鸡有话说:
具体的putchar和getchar的用法请参考书本P72 3.6部分
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char c1 = 0;
char c2 = 0;
c1 = getchar();
c2 = getchar();
putchar(c1);
putchar(c2);
printf("\n");
printf("%c%c", c1, c2);
return 0;
}
运行结果:
注意事项:
-
在输入时,不能打空格,那是因为getchar()函数读入两个字符,而空格也是字符,如果输入了a<空格>b,那么getchar读入的是a和空格,而不是a和b
-
类似的,如果我们a回车b,getchar函数也是读入a和回车,回车也是一个字符:‘\n’
回答:
(1)变量c1和c2定义为字符型还是整型?或二者皆可?
- 两者皆可,int和char都可以,char类型本质上以ASCII码存储,而ASCII码本身就是整数
(2)要求输出c1和c2的ASCII码,应如何处理?
-
直接用printf函数输出即可,用%d格式符
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { char c1 = 0; char c2 = 0; c1 = getchar(); c2 = getchar(); printf("%c %c\n", c1, c2); printf("%d %d\n", c1, c2); return 0; }
运行结果如下:
(3)整形变量和字符变量是否在任何情况下都可以互相代替?char c1, c2;和int c1, c2;是否无条件等价?
不是的。
整型变量要在可输出字符范围内(ASCII码为0-255字符)是可以互相转换的,但是如果整型变量的值在可输出字符范围之外,是不能代替字符变量的
第四章课后习题
4.1 算术&关系&逻辑运算
题目概述:
- 什么是算术运算?
- 算术运算就是指加减乘除和整数的模运算(取余数运算)
- 这些都是我们从小到大学的,无非就是四则运算
- 什么是关系运算?
- 关系运算就是比较运算,将两个数值进行比较,判断其比较结果是否符合给定的条件
- 请参考课本P103 4.1.2 关系运算符和关系表达式
- 什么是逻辑运算?
- 逻辑运算指两个条件进行运算,有逻辑与、逻辑或、逻辑非三种
- 请参考课本P103 4.1.3 逻辑运算符和逻辑表达式
4.2 真假问题
题目概述:
C语言中如何表示“真”和“假”? 系统如何判断一个量的“真”和“假”?
答:
如果有一个逻辑表达式,若其值为“真”,系统会以 1表示,若其值为“假”,会以 0表示。但是在判断一个逻辑量的值时,系统会以 0作为“假”,以非 0作为“真”。例如
3 &&5的值为“真”,系统给出 3&&5的值为1。
4.3 计算逻辑表达式
题目概述:
本题若出现错误,欢迎纠错!
写出下面各逻辑表达式的值,设a=3,b=4,c=5
-
a + b > c && b == c
- 先看&&左边 :a + b > c → 3 + 4 > 5 → 7 > 5 为真
- 再看&&右边:b == c ? 4 == 5? 为假
- 对于&&运算符,有1个假最终结果为0
- 答案:0
-
a || b + c && b - c
- 先看&&左边: 先优先计算算术运算符,也就是 a||9,为真
- 再看&&右边: b-c = -1,是一个非0数字,非0也是真
- 对于&&运算符,两个都是真,最终结果为真
- 答案:1
-
!(a > b) && !c || 1
- 先看&&左边:!优先级最高,!(3>4)为真
- 再看&&右边:!优先级最高,!c为假||1为真,对于||中,有1真则真
- 对于&&运算符,两个都是真,最终结果为真
- 答案:1
-
!(x = a) && (y = b) && 0
- 这里一看有很多(),他们的优先级最高,但是我们看最后有个0,0则假
- 对于&&运算符:只要有一个为假整体为假
- 答案:0
-
!(a + b) + c - 1 && b + c / 2
- 先看&&左边:!(a + b)为0, 0 + c →5 为真
- 再看&&右边:b + c / 2 为真
- 对于&&运算符:只要有一个为假整体为假
- 答案:1
4.4 分段函数问题
待完善。
4.5 三个整数求最大值
题目概述:
由键盘输入3个整数a、b、c,要求输出其中最大的数,请编写程序
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
int c = 0;
int max = 0;
scanf("%d %d %d", &a, &b, &c);
if (a < b)
{
if (b < c)
max = c;
else
max = b;
}
else
{
if (a > c)
max = a;
else
max = c;
}
printf("%d", max);
return 0;
}
- 答案不唯一,这个只是自己写的其中一个判断方法,具体思路是
- 先判断a是否大于b,如果a小于b成立的话,进入if语句,判断b是否比c大,如果b比c大,那么最大的就是b,否则就是c
- 如果a小于b不成立,那么走else语句,判断a是否大于c,如果a比c大,那么最大值就是a,否则是c
- 进入else语句无需再判断a是否大于b,因为在if语句语句判断过了
4.6 成绩赋级
题目概述:
给出一个百分制的成绩,要求输出成绩等级’A’ ‘B’ ‘C’ ‘D’ ‘E’。90 分以上为 A,80~89 分为 B,70~79 分为 C,60~69 分为 D,60 分以下为 E。
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
float score = 0;
char level = 0;
scanf("%f", &score);
if (score <= 100 && score >= 0)
{
switch ((int)(score / 10))
{
case 10:
case 9:
level = 'A';
break;
case 8:
level = 'B';
break;
case 7:
level = 'C';
break;
case 6:
level = 'D';
break;
default:
level = 'E';
}
printf("level:%c", level);
}
else
{
printf("输出错误");
}
return 0;
}
-
这题最好输入的是浮点数,因为你的成绩不一定都是整数,有可能带个小数点比如说99.5
-
输入后,最好先用if-else判断分数的合法性,因为你的分数是不可能超过100(暂定满分100)或者是小于0分的
-
如果用switch语句,需要注意的是:switch语句中的表达式只能是整型和字符型,而不能是浮点型和字符串类型
-
那么如果我们非要用switch语句怎么办,很简单,只需要强制类型转换
-
强制类型转换是把变量从一种类型转换为另一种数据类型。例如,本题中的浮点型变量float如果想存储一个 int类型的值到switch语句判断,只需要把float类型强制转换为 int 类型。使用强制类型转换运算符来把值显式地从一种类型转换为另一种类型,如下所示:
(type_name) expression ↓ (int)(score / 10);
-
-
4.7 蹂躏正整数
题目概述:
给1个不多于5位的正整数,要求:
①求出他是几位数
②分别输出每一位数字
③按逆序输出各位数字,例如原数321,应输出123
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int n = 0;
int count = 0;//位数
int m = 0;
int ten_thousand = 0;//万位
int thousand = 0;//千位
int hundred = 0;//白位
int ten = 0;//十位
int indiv = 0;//个位
scanf("%d", &n);
if (n <= 99999)
{
//计算位数
if (n > 9999)
count = 5;
else if (n > 999)
count = 4;
else if (n > 99)
count = 3;
else if (n > 9)
count = 2;
else
count = 1;
printf("count = %d\n", count);
//计算每个位数分别的数字是多少
ten_thousand = n / 10000;
thousand = (n / 1000) % 10;
hundred = (n / 100) % 10;
ten = (n / 10) % 10;
indiv = n % 10;
//逆序输出
switch (count)
{
case 5:
printf("%d%d%d%d%d", indiv,ten,hundred,thousand,ten_thousand);
break;
case 4:
printf("%d%d%d%d", indiv, ten, hundred, thousand);
break;
case 3:
printf("%d%d%d", indiv, ten, hundred);
break;
case 2:
printf("%d%d", indiv, ten);
case 1:
printf("%d", indiv);
}
}
else
{
printf("超出题目要求");
}
return 0;
}
运行结果如下:
- 如果用选择语句来做本题的话,其实非常的反人类
- 需要先计算出位数,也就是count,记录输入的数字占了多少位数
- 然后分别算出每一个位数的值,也就是12345位置中,占万位的是1,千位的是2…,算出来之后才能用case语句逆序输出
- 最后分别根据位数来输出不同位级的逆序输出(比如说5位数走case 5输出5位数,比如说4位数走case 4输出4位数)
- 因为本章节是选择语句的章节,所以才用选择语句来做,其实用循环语句更加方便,如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int n = 0;
int count = 0;
int m = 0;
scanf("%d", &n);
if (n <= 99999)
{
while (n != 0)
{
count++;
m = n % 10;
printf("%d", m);
n /= 10;
}
printf("\n");
printf("n的位数是:%d", count);
}
else
{
printf("超出了5位数要求");
}
return 0;
}
运行结果如下:
4.8 人体CPU干烧题
前言:后面这三题之所以独立出来是感觉这三题是数学题,本人数学实力有限,所以这三题代码部分基本上都出自于官方教材的课后习题答案部分。
题目概述:
企业发放的奖金根据企业的当年利润决定。
当利润(以 I表示) ****
- 低于或等于10 0000 元时,奖金可提 10%;
- 利润大于 10 0000 元,小于 20 0000 元(10 0000<I≤20 0000)时
- 低于 10 0000 元的部分按 10%提成
- 高于 10 0000 元的部分,可提成 7.5%
- 利润大于 20 0000元,小于或等于40 0000 元(20 0000<I≤40 0000)时
- 低于 20 0000 元的部分仍上述办法提成(下同)。
- 高于 20 0000 元的部分按 5%提成。
- 利润大于 40 0000 元,小于或等于 60 0000 元(40 0000<I≤60 0000)时
- 高于 40 0000 元的部分按 3%提成。
- 利润大于 60 0000 元,小于或等于 100 0000 元(60 0000<I≤100 0000)时
- 高于60 0000 元的部分按 1.5%提成
- 利润大于 100 0000 时,超过 100 0000 元的部分按 1%提成
从键盘输人当年利润 I,求应发奖金总数。
要求:
(1)用if语句编程序;
(2)用switch语句编程序。
野鸡有话说:
代码难度:⭐
理解题目难度:⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
PS:本题我搞了一下午都没看懂,放弃了,所以下面的答案不是我写的,是课本配套的习题解答,给你们研究研究
代码如下:
switch形式的计算
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
long i = 0;
double bonus, bon1, bon2, bon4, bon6, bon10;
int branch = 0;
bon1 = 100000 * 0.1;
bon2 = bon1 + 100000 * 0.075;
bon4 = bon2 + 200000 * 0.05;
bon6 = bon4 + 200000 * 0.03;
bon10 = bon6 + 400000 * 0.015;
printf("请输入利润i:");
scanf("%ld", &i);
branch = i / 100000;
if (branch > 10)
branch = 10;
switch (branch)
{
case 0:
bonus = i * 0.1;
break;
case 1:
bonus = bon1 + (i - 100000) * 0.075;
break;
case 2:
case 3:
bonus = bon2 + (i - 200000) * 0.05;
break;
case 4:
case 5:
bonus = bon4 + (i - 400000) * 0.03;
break;
case 6:
case 7:
case 8:
case 9:
bonus = bon6 + (i - 600000) * 0.015;
break;
case 10:
bonus = bon10 + (i - 1000000) * 0.01;
}
printf("奖金是:%10.2f\n",bonus);
return 0;
}
if形式的计算
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
long i = 0;
double bonus, bon1, bon2, bon4, bon6, bon10;
bon1 = 100000 * 0.1;
bon2 = bon1 + 100000 * 0.075;
bon4 = bon2 + 100000 * 0.05; //这里是错的??为什么跟上面的不一样??
bon6 = bon4 + 100000 * 0.03;
bon10 = bon6 + 400000 * 0.015;
printf("请输入利润i:");
scanf("%ld", &i);
if (i <= 100000)
bonus = i * 0.1;
else if (i <= 200000)
bonus = bon1 + (i - 100000) * 0.075;
else if (i <= 400000)
bonus = bon2 + (i - 200000) * 0.05;
else if (i <= 600000)
bonus = bon4 + (i - 400000) * 0.03;
else if (i <= 1000000)
bonus = bon6 + (i - 600000) * 0.015;
else
bonus = bon10 + (i - 1000000) * 0.01;
printf("奖金是:%10.2f\n",bonus);
return 0;
}
4.9 圆塔问题
题目概述:
有 4 个圆塔,圆心分别为(2,2)、(-2,2)、(-2,-2)、(2,-2),圆半径为 1,见图 4.14。这 4 个塔的高度为10m.塔以外无建筑物。现输入任一点的坐标。求该点的建筑高度(塔外的高度为零).
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int h = 10;
float x1 = 2;
float y1 = 2;
float x2 = -2;
float y2 = 2;
float x3 = -2;
float y3 = -2;
float x4 = 2;
float y4 = -2;
float x, y, d1, d2, d3, d4;
scanf("%f,%f", &x, &y);
d1 = (x - x1) * (x - x1) + (y - y1) * (y - y1);
d2 = (x - x2) * (x - x2) + (y - y2) * (y - y2);
d3 = (x - x3) * (x - x3) + (y - y3) * (y - y3);
d4 = (x - x4) * (x - x4) + (y - y4) * (y - y4);
if (d1 > 1 && d2 > 1 && d3 > 1 && d4 > 1)
h = 0;
printf("该点高度为%d\n", h);
return 0;
}
本题其实就是让你算该点到每个中心点的距离,只要点在塔外那么就是0,只要在塔内就是10.
4.10 求ax2 + bx + c = 0方程的解
题目概述
根据代数知识,应该有以下几种可能:
- a=0,不是二次方程,而是一次方程
- b2-4ac=0,有两个相等的实根
- b2-4ac>0,有两个不等的实根
- b2-4ac<0,有两个共扼复根。
- 请画出 N-S 流程图,并据此编写程序,程序应能处理上面 4 种情况。运行程序时,分别给出不同的 a、b、c 值,相应于上面 4 种情况,分析输出结果。
N-S图:
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
int main()
{
float a, b, c, disc, x1, x2, realpart, imagpart;
printf("please enter a,b,c:");
scanf("%f,%f,%f", &a, &b, &c);
printf("The equation ");
if (fabs(a) <= 1e-6)
printf("is not a quadratic\\n");
else
{
disc = b * b - 4 * a * c;
if (fabs(disc) <= 1e-6)
printf("has two equal roots:%8.4f\n", -b / (2 * a));
else if (disc > 1e-6)
{
x1 = (-b + sqrt(disc)) / (2 * a);
x2 = (-b - sqrt(disc)) / (2 * a);
printf("has distinct real roots:%8.4f and 68.4fn", x1, x2);
}
else
{
realpart = -b / (2 * a);
imagpart = sqrt(-disc) / (2 * a);
printf("has complex roots:\n");
printf("%8.4f+%8.4fi\n", realpart, imagpart);
printf("%8.4f-%8.4fi\n", realpart, imagpart);
}
}
return 0;
}
本题答案亦出自教材课后习题的代码。
第五章课后习题
5.1 求100-200的素数
题目概述:
求100-200之间的素数
题目思路:
- 首先要求100-200之间的素数,首先你得要产生100-200的数,这样就用到循环。
- 有了数字之后,我们要判断一个数是否是素数,首先要知道素数是什么
- 素数:大于1的整数中,只能被1或者被自己整除的数
- 既然知道了怎么样的数是素数,我们就可以开始处理了
- 100 - 200的数一个一个去判断是否是素数,就需要用到循环遍历100-200的数,用i表示
- 开始判断i是否为素数
- 我们可以假设每个数都是素数,用flag来表示,1就是素数,0不是素数,然后最后把是素数的打印出来
- 然后我们再使用一个循环,让i除2到i-1之间的数,
- 如果有一个数能被2到i-1之间的数字整除,那么这个数就不是素数
- 此时我们用一个选择语句来判断是否被相除为0了,如果为0就不是素数,既然不是素数我们就可以跳出循环,进行下一个i的判断
- 如果有一个数不能被2到i-1之间的数字整除,那么这个数就是素数
- 如果有一个数能被2到i-1之间的数字整除,那么这个数就不是素数
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
int flag = 0;
for (i = 100; i <= 200; i++)
{
flag = 1;
for (j = 2; j < i; j++)
{
if (i % j == 0)
{
flag = 0;
break;
}
}
if (flag == 1)
printf("%d ", i);
}
return 0;
}
但是这代码是可以优化的:
优化1:由于偶数都不是素数,我们可以只从奇数项开始,每个数加+2让下一个也是奇数
优化2:当一个数不是素数的时候,一定能下成下列的一个式子,且他是可以被一个因子整除的
例:m = a *b
16 = 4 * 4
16 = 2 * 8
a和b中一个数字是 <= sqrt(m) →[4]
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
int main()
{
int i = 0;
int count = 0;
for (i = 101; i <= 200; i += 2)
{
int j = 0;
int flag = 1;
for (j = 2; j <= sqrt(i); j++)
{
if (i % j == 0)
{
flag = 0;
break;
}
}
if (flag == 1)
{
printf("%d ", i);
}
}
return 0;
}
运行结果如下:
5.2 输入字符统计中英数个数
题目概述:
输人一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数。
题目思路:
- 首先因为我们要输入不止一个的字符,所以可以用循环来输入多个字符
- 还记得教材第三章学的字符输入输出函数吗?也就是putchar和getchar在本题就使用上了。
- 我们一般在输入的时候都是以回车来作为结束标志,所以我们可以用getchar函数来做判断条件,当getchar函数读到了’\n’(回车)的时候结束循环
- 循环体内依次来判断每个字符的分类,题目要求四个分类 英文字母、空格、数字和其他字符,我们可以用if来判断。
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char c = 0;
int letter = 0; //字母个数
int space = 0; //空格个数
int num = 0; //数字个数
int other = 0; //其他字符个数
while ((c = getchar()) != '\n')
{
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
letter++;
else if (c >= '0' && c <= '9')
num++;
else if (c == ' ')
space++;
else
other++;
}
printf("字母的个数是:%d\n", letter);
printf("数字的个数是:%d\n", num);
printf("空格的个数是:%d\n", space);
printf("其他的个数是:%d\n", other);
return 0;
}
运行结果如下:
5.3 打印水仙花数
题目概述:
输出所有的“水仙花数”,所谓“水仙花数”是指一个 3 位数,其各位数字立方和等于该数本身。
例如,153 是一水仙花数,因为 153=1^3 +5^3 +3^3
题目思路:
- 首先水仙花数是三位数的,要输出所有的水仙花数,就要用到循环。
- 剩下就简单了,我们直接把他的百位、十位、个位一个一个剥离出来
- 剥离出来后用水仙花数的判断条件来判断是否是水仙花数,本题中我没有用pow函数而是直接让每个位乘以自己三次就是三次方了。
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int i = 100;
int hundred = 0;
int ten = 0;
int indiv = 0;
for (i = 100; i <= 999; i++)
{
hundred = i / 100;
ten = (i % 100) / 10;
indiv = (i % 100) % 10;
if ((hundred * hundred * hundred + ten * ten * ten + indiv * indiv * indiv) == i)
printf("%d ", i);
}
return 0;
}
运行结果如下:
5.4 猴子吃桃
题目概述:
猴子吃桃问题。猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了个。第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第 10 天早上想再吃时,就只剩一个桃子了。求第一天共摘多少个桃子。
题目思路:
- 这道题首先要先逆推一下:
- 第十天的时候只剩下一个,结合题目:以后每天早上都吃了前一天剩下的一半零一个,可推出
- day10 = (day9 / 2) - 1,推出
- day9 = (day10+1)* 2
- 再算第八天
- day8 = (day9 + 1) * 2
- 我们用x1表示当天,x2表示x1的前一天得出代码:
- 第十天的时候只剩下一个,结合题目:以后每天早上都吃了前一天剩下的一半零一个,可推出
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int day = 9; //因为第10天是1个,是已知的
int x1 = 1; //第10天的桃子为1
int x2 = 0;
while (day > 0)
{
x2 = (x1 + 1) * 2;
x1 = x2;
day--;
}
printf("%d", x2);
return 0;
}
运行结果如下:
5.5 弹弹弹
题目概述:
1个球从 100m 高度自由落下,每次落地后反跳回原高度的一半,再落下,再反弹,求它在第 10 次落地时,共经过了多少米?第 10 次反弹多高?
题目思路:
-
根据题意划出图解
- ps:具体小球是怎么样下落反弹的请自行想象,本题只是做一个拆解,不代表他就是这样下落的。
-
本题需要注意,一个球一直跳回原高度的一半,肯定会出现小数,所以要用浮点型来进行计算
-
然后我们循环进行每次小球的距离即可
- 计算小球从高处下落到地上的距离
- 然后在计算反弹的距离
- 然后再把下落和反弹的距离加上,就是本次小球经过的米数
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
double high = 100.0;
double meter = 0.0;
int i = 0;
for (i = 1; i <= 10; i++)
{
meter += high; //小球从高处下落到地上的距离
high /= 2; //小球从地上反弹的距离
meter += high; //小球从该次从高处下落+地上到从地上反弹的距离
}
meter -= high; //减去反弹的距离,就是题目第十次到地上的距离
printf("high is : %lf\n", high);
printf("meter is : %lf\n", meter);
return 0;
}
运行结果如下:
- 本题需要注意读题的细节,题目问的是第10次落地时,共经过了多少米,而没有说第十次反弹经过了多少米,所以我们在计算完之后要记得减去第10次反弹的高度
5.6 输出菱形图案
题目概述:
输出一下图案
题目思路:
本题我们可以做一个通用的打印菱形
我们把菱形分成这样
然后分成上半和下半,开始找规律
先看上半
i | 空格数量 | 星号 |
---|---|---|
0 | 3 | 1 |
1 | 2 | 3 |
2 | 1 | 5 |
3 | 0 | 7 |
推出公式 | line - 1 - i | 2 * i + 1 |
- line表示多少行。
得出上半部分代码
for (i = 0; i < line; i++)
{
for (j = 0; j < (line - 1 - i); j++)
{
printf(" ");
}
for (j = 0; j < (2 * i + 1); j++)
{
printf("*");
}
printf("\n");
}
运行结果如下:
再看下半
i | 空格数量 | 星号 |
---|---|---|
0 | 1 | 5 |
1 | 2 | 3 |
2 | 3 | 1 |
推出公式 | i + 1 | 2*(line-1-i)-1 |
得出下半部分代码:
for (i = 0; i < (line - 1); i++)
{
for (j = 0; j < (i + 1); j++)
{
printf(" ");
}
for (j = 0; j < 2 * (line - 1 - i) - 1; j++)
{
printf("*");
}
printf("\n");
}
运行结果如下:
然后分别打印出上下半即可。
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int i = 0;
int line = 0;
int j = 0;
scanf("%d", &line);
for (i = 0; i < line; i++)
{
for (j = 0; j < (line - 1 - i); j++)
{
printf(" ");
}
for (j = 0; j < (2 * i + 1); j++)
{
printf("*");
}
printf("\n");
}
for (i = 0; i < (line - 1); i++)
{
for (j = 0; j < (i + 1); j++)
{
printf(" ");
}
for (j = 0; j < 2 * (line - 1 - i) - 1; j++)
{
printf("*");
}
printf("\n");
}
return 0;
}
运行结果如下:
- 以上是通用输入菱形,对于本题来说,输入4行,那么就能打印出本题要求的菱形。
- 如果不需要输入直接打印,那么上半有4行,我们把line=4,代入本题条件即可,具体如下。
本题代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int i = 0;
int line = 0;
int j = 0;
for (i = 0; i < 4; i++)
{
for (j = 0; j < (3 - i); j++)
{
printf(" ");
}
for (j = 0; j < (2 * i + 1); j++)
{
printf("*");
}
printf("\n");
}
for (i = 0; i < 3; i++)
{
for (j = 0; j < (i + 1); j++)
{
printf(" ");
}
for (j = 0; j < 2 * (3 - i) - 1; j++)
{
printf("*");
}
printf("\n");
}
return 0;
}
- 只需要修改一下循环条件即可完成本题。
5.7 找球手
题目概述:
两个乒乓球队进行比赛,各出3 人。甲队为 A、B、C 3 人,乙队为 X、Y、Z 3 人。已抽签决定比赛名单。有人向队员打听比赛的名单,A 说他不和 X 比,C 说他不和 X、Z 比,请编写程序找出 3 对赛手的名单。
题目思路:
-
由题意得,画图得出以下关系
-
然后我们还得知道,由于这个比赛都是1v1,不可能出现A又要打X又要打Y的情况,所以还得出了一个条件是
A != B && A!=C && B!=C
-
用A、B、C一一列举对手,直到满足判断条件为止即可。
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int A = '0';
int B = '0';
int C = '0';
//整型和字符型是可以相互转换的,所以int和char皆可。这里用int,因为要用来执行判断条件
for (A = 'X'; A <= 'Z'; A++)
{
for (B = 'X'; B <= 'Z'; B++)
{
for (C = 'X'; C <= 'Z'; C++)
{
if (A != B && A != C && B != C && A != 'X' && C != 'X' && C != 'Z')
{
printf("A VS %c\n", A);
printf("B VS %c\n", B);
printf("C VS %c\n", C);
}
}
}
}
return 0;
}
运行结果如下:
第六章课后习题
6.1 求学生平均成绩
题目概述:
一个班有 10 个学生的成绩,要求输人这 10 个学生的成绩,然后求出它们的平均成绩
题目思路:
- 本题只要知道数组怎么用就能做出本题
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
float sum = 0.0;
int i = 0;
float arr[10] = {0.0};
for (i = 0; i < 10; i++)
{
scanf("%f", &arr[i]);
}
for (i = 0; i < 10; i++)
{
sum += arr[i];
}
printf("%.2f", sum / 10);
return 0;
}
6.2 6.1中找出成绩最高
题目概述:
在上题基础上求出平均成绩最高的课程(以课程序号表示)及其成绩。
讲真这道题我不知道他在问什么?他要求平均成绩最高的课程,但是上题我们求的是每个学生单科成绩的平均成绩而不是平均成绩中最高的课程???所以我不明白他在问什么,求解答。
6.3 找出最高成绩的同学
题目概述:
已知一个班 10 个学生的成绩,存放在一个一维数组中,要求找出其中成绩最高的学生的成绩和该生的序号。
题目思路:
- 这道题只需要在6.1的基础上修改一下即可,我们假设最高分的就是第一个同学,然后循环类比只要有比他高的就把最高分的分数赋值给他即可
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
float sum = 0.0;
int i = 0;
float arr[10] = {0.0};
float max = 0.0;
for (i = 0; i < 10; i++) //输入数据
{
scanf("%f", &arr[i]);
}
for (i = 0; i < 10; i++) //开始比较
{
if (arr[i] > max)
{
max = arr[i];
}
}
printf("%d\n", i);
printf("%.2f", arr[i - 1]);
return 0;
}
6.4 求平均成绩
题目概述:
有 3 个学生,上4门课,要求输入全部学生的各门课成绩,并分别求出每门课的平均成绩。
题目思路:
- 首先本题说明了有3名学生,上四门课,我们要存放每一位学生的课程成绩,用二维数组是最好的
- 我们用i代表学生,j代表课程,遍历数组输入数据,本题代码部分提供了一组输入数据,便于运行时无需自行输入
- 输入完数据后,我们要算平均成绩,就要把每一个学生的当科成绩加起来再除以3,
- 我们只需要固定j不变,j不变就代表一直都是当科的成绩,只需要循环遍历i就能得到三个考生在同一门课程的成绩总和,用不同的变量存放,最后在输出时除以3就能得到平均成绩
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
/*
94 78 87 96
66 87 75 69
100 98 89 77
提供一组输入数据
*/
int main()
{
int i = 0;
int j = 0;
float sum_a = 0.0;
float sum_b = 0.0;
float sum_c = 0.0;
float sum_d = 0.0;
float arr[3][4] = { 0.0 };
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
scanf("%f", &arr[i][j]);
}
}
for (i = 0; i < 3; i++)
{
sum_a += arr[i][0];
sum_b += arr[i][1];
sum_c += arr[i][2];
sum_d += arr[i][3];
}
printf("first average:%.2f\n", sum_a / 3);
printf("second average:%.2f\n", sum_b / 3);
printf("third average:%.2f\n", sum_c / 3);
printf("forth average:%.2f\n", sum_d / 3);
return 0;
}
优化:
- 后面我发现上段代码是可以优化的,我们可以创建1个4*4的数组,当i=4时,用来存放平均成绩
- 这样就可以优化第二段循环代码,我们固定j不变,可以用嵌套循环先循环j再循环i,如下,这样代码会比上端代码更加灵活。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
/*
94 78 87 96
66 87 75 69
100 98 89 77
提供一组输入数据
*/
int main()
{
int i = 0;
int j = 0;
float sum = 0.0;
float arr[4][4] = { 0.0 };
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
scanf("%f", &arr[i][j]);
}
}
for (j = 0; j < 4; j++)
{
sum = 0.0;
for (i = 0; i < 3; i++)
{
sum += arr[i][j];
}
arr[3][j] = sum / 3;
printf("%d average:%.2f\n", j + 1, arr[3][j]);
}
return 0;
}
运行结果如下:
6.5 求平均成绩排名
题目概述:
已知5 个学生的4 门课的成绩,要求求出每个学生的平均成绩,然后对平均成绩从高到低将各学生的成绩记录排序(成绩最高的学生的排在数组最前面的行,成绩最低的学生的排在数组最后面的行)。
题目思路:
- 本题较为复杂,需要先计算出平均成绩然后还要进行排序,所以本题会分为两个部分
- 算出平均成绩
- 排序
Part 1:算出平均成绩
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
float arr[5][4] = { {94,78,87,76},{66,87,75,69},{100,98,89,77},{92,58,72,84},{82,73,67,54} };
int i = 0;
int j = 0;
float sum = 0;
int max = 0;
float average[5] = { 0.0 };
for (i = 0; i < 5; i++)
{
sum = 0.0;
for (j = 0; j < 4; j++)
{
sum += arr[i][j];
}
average[i] = sum / 4;
}
for (i = 0; i < 5; i++)
{
printf("%.2f", average[i]);
printf("\n");
}
return 0;
}
运行结果如下:
Part 2:对平均成绩进行排序
本题排序我们用选择排序法,而且是从大到小的选择排序法。
既然用选择排序法,那么选择排序法是什么呢?(以从大到小为例),具体的讲解可以看看别人的从小到大的排序法,我这边是基于从小到大的排序法对本题进行变种
- 选择排序的基本思想是,首先,选出最大的数,放在第一个位置;然后再选出第二小的数,放在第二个位置,再选出第三小的数,放在第三个位置,以此类推。直到所有的数从大到小排序。
- 很明显,要实现选择排序,必须要用到循环。
代码如下:
//选择排序
sz = sizeof(average) / sizeof(average[0]); //算出average的元素个数
for (i = 0; i < sz; i++)
{
max = i;//假设i是最大值下标
//找出最大值的数组下标
for (j = 1 + i; j < sz - 1; j++) //这个解释有点长,详情请看①解释
{
if (average[j] > average[max])
{
max = j; //如果发现arr[j]中的值比arr[max]大,就记录最大值的下标
}
}
//找出最大值下标后,赋值
if (i != max)
{
temp = average[max];
average[max] = average[i];
average[i] = temp;
}
}
for (i = 0; i < 5; i++)
{
printf("%.2f", average[i]);
printf("\n");
}
return 0;
}
- ①由于我们已经假设了i是最大的,也就是arr[0]是最大值,那么j应该从1开始,但是我们每次循环完之后,会把最大值赋给数组最前面的下标,那么等下一趟的时候,其实第一个元素不需要再遍历了,所以j要在+1的基础上再加上一个i,如果我解释的不明白的话可以看看下面的图解
图解实现:
PS:绿色为固定不变的数字,橙色为该趟排序的一开始假定最大值,红色为该趟排序中找到的最大值
第一趟,选择排序
i = 0时
- 忘记改了,记录数组下标,把j赋给max,下同
第二趟选择排序,i=1
- 看表格中的绿色部分,因为经过第一趟选择排序后,遍历了整个数组记录了数组中的最大值,那么91分是整个数组的最大值,这个毋庸置疑,所以在第二趟选择排序的时候,arr[0]就不参与这一次乃至后面的排序了,只需要对剩下四个数组元素排序他们的大小即可
第三趟选择排序,i=2
最后一趟不演示了
最终代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
float arr[5][4] = { {94,78,87,76},{66,87,75,69},{100,98,89,77},{92,58,72,84},{82,73,67,54} };
int i = 0;
int j = 0;
float sum = 0;
float average[5] = { 0.0 };
//选择排序需要用到的变量
float temp = 0;
int max = 0;
int sz = 0;
//计算平均值
for (i = 0; i < 5; i++)
{
sum = 0.0;
for (j = 0; j < 4; j++)
{
sum += arr[i][j];
}
average[i] = sum / 4;
}
//选择排序
sz = sizeof(average) / sizeof(average[0]); //算出average的元素个数
for (i = 0; i < sz; i++)
{
max = i;//假设i是最大值下标
//找出最大值的数组下标
for (j = 1 + i; j < sz - 1; j++)
{
if (average[j] > average[max])
{
max = j; //如果发现arr[j]中的值比arr[max]大,就记录最大值的下标
}
}
//找出最大值下标后,赋值
if (i != max)
{
temp = average[max];
average[max] = average[i];
average[i] = temp;
}
}
for (i = 0; i < 5; i++)
{
printf("%.2f", average[i]);
printf("\n");
}
return 0;
}
运行结果如下:
6.6 逆序存放数组
题目概述:
将一个数组中的值按逆序重新存放。例如,原来顺序为 8,6,5,4,1。要求改为 1,4,5,6,8
题目思路:
- 本题只需要知道数组的最后一个元素即可
- 用最后的元素和最初的元素交换,如何访问到数组最后一个元素?就要使用到sizeof了
- sizeof关键字:是C语言的关键字,是可以计算类型或者变量大小的,同时也可以计算数组的大小
- 我们只需要知道,sizeof(数组名):计算的是整个数组的大小,单位是字节,比如说本题,sizeof(arr),
- int arr[5]:有5个元素,每个元素类型都是int类型,所以得出数组总大小为:5*4=20
- 然后我们在计算sizeof(arr[0]):计算的是数组中元素个数的类型,单位是字节,比如说arr[0]是int类型,也就是4个字节
- 最后我们sizeof(arr) / sizeof(arr[0])得出数组元素个数,也就是5,放入sz变量中
- 我们只需要知道,sizeof(数组名):计算的是整个数组的大小,单位是字节,比如说本题,sizeof(arr),
- sizeof关键字:是C语言的关键字,是可以计算类型或者变量大小的,同时也可以计算数组的大小
- 用最后的元素和最初的元素交换,如何访问到数组最后一个元素?就要使用到sizeof了
- 然后就好做了,但是arr[sz]就是数组的最后一个元素吗?数组下标是从0开始的,所以arr[sz-1]才是真正的数组最后一个元素
- 由于循环完了一次i++,那么arr[1]就要跟arr[sz-1]的前一个元素交换顺序,但是我们应该如何交换呢?我们可以直接arr[sz-1-i],那么当i=0的时候,arr[0]就跟arr[5-1-0]交换,也就是arr[0] 和 arr[4],当i=1的时候,arr[1]和arr[5-1-1]交换,也就是arr[1]和arr[3]…以此类推
- 最后,由于是两两交换,所以我们不需要遍历整个数组,只要遍历半个就能完成交换,否则遍历完整个数组会把逆序的又逆回来,得到的还是86541
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
/*将一个数组中的值按逆序重新存放。例如,原来顺序为 8,6,5,4,1。要求改为 1,4,5,6,8*/
int main()
{
int arr[] = { 8,6,5,4,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int temp = 0;
for (i = 0; i < sz / 2; i++)
{
temp = arr[i];
arr[i] = arr[sz - 1 - i];
arr[sz - 1 - i] = temp;
}
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果如下:
6.7 输出图案
题目概述:
输出如下图案:
题目思路:
- 由图得,我们发现后面星号空格组合是不变的,变的是前面的空格,所以我们只需要处理前面的空格即可
- 我们可以划一条线计算一下每一行空格的数量
由图可得,推出公式:空格数量是2i
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
for (i = 0; i < 5; i++)
{
for (j = 0; j < 2 * i; j++)
{
printf(" ");
}
printf("* * * * *\n");
}
return 0;
}
运行结果如下:
6.8 短文统计字符
题目概述:
有一篇短文,共有 3 行文字,每行有 80 个字符。想统计出其中英文大写字母,小写字母,数字、空格以及其他字符各有多少个。
题目思路:
- 本题难点在输入数据
- 因为我们输入的是字符串,定义了一个char类型的数字,但是我们是不能用scanf输入的,原因是:
- scanf在输入字符\字符串时当遇到空格的时候就认为结束输入,导致后面的数字无法读入。
- 就算那是scanf可以输入整个字符串,我们也不可能一个一个遍历(j = 0;j<80;j++)输入吧,这个时候就需要用到gets函数
- gets函数:
- C 库函数 char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
- 只要知道gets函数是输入字符串的,遇到‘\0’就结束
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
/*有一篇短文,共有 3 行文字,每行有 80 个字符。
想统计出其中英文大写字母,小写字母,数字、空格以及其他字符各有多少个。*/
#include <stdio.h>
int main()
{
char ch[3][80] = { '0' };
int i = 0;
int j = 0;
int upper = 0;
int lower = 0;
int number = 0;
int other = 0;
int space = 0;
for (i = 0; i < 3; i++)
{
gets(ch[i]);
}
for (i = 0; i < 3; i++)
{
for (j = 0; ch[i][j] != '\0'; j++)
{
if (ch[i][j] >= 'A' && ch[i][j] <= 'Z')
{
upper++;
}
else if (ch[i][j] >= 'a' && ch[i][j] <= 'z')
{
lower++;
}
else if (ch[i][j] >= '0' && ch[i][j] <= '9')
{
number++;
}
else if (ch[i][j] == ' ')
{
space++;
}
else
{
other++;
}
}
}
printf("upper:%d\n", upper);
printf("lower:%d\n", lower);
printf("num:%d\n", number);
printf("space:%d\n", space);
printf("other:%d\n", other);
return 0;
}
6.9 翻译密码
题目概述:
有一行电文,已按下面规律译成密码:
A->Z a->z
B->Y b->y
C->X c->x
即第 1个字母变成第 26 个字母,第 2个字母变成第 25 个字母,第i个字母变成第(26-i+1)个字母。非字母字符不变。假如已知道密码是 Umtorhs,要求编程序将密码译回原文,并输出密码和原文。
题目思路:
- 本题感觉也是找规律,我们看ASCII码表
看大写字母部分,65-90就是大写字母的部分,加起来再减去密文,就能得到大写字母解码的部分,本题其实可以将65+90加起来到代码上,但是为了更清晰,采用了’A‘+’Z‘
对于小写字母,97-122就是小写字母部分,加起来再减去密文,就能得到小写字母解码部分
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char ch[100] = { '0' };
gets(ch);
int i = 0;
for (i = 0; ch[i] != '\0'; i++)
{
if (ch[i] >= 'A' && ch[i] <= 'Z')
{
ch[i] = 'A' + 'Z' - ch[i];
}
else if (ch[i] >= 'a' && ch[i] <= 'z')
{
ch[i] = 'a' + 'z' - ch[i];
}
else {
ch[i] = ch[i];
}
}
printf("%s", ch);
return 0;
}
运行结果如下:
6.10 拼接字符串
题目概述:
编写一程序,将两个字符串连接起来,
- 用 strcat 函数
- 不用 strcat 函数
题目思路:
Part 1 用strcat函数
首先知道这个函数的用法
-
声明
- 下面是 strcat() 函数的声明。
char *strcat(char *dest, const char *src)
-
参数
-
dest – 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。
-
src – 指向要追加的字符串,该字符串不会覆盖目标字符串。
-
-
本题的目标数组就是s1,追加的字符串就是s2
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char s1[100] = { '0' };
char s2[50] = { '0' };
gets(s1);
gets(s2);
strcat(s1, s2);
printf("%s", s1);
return 0;
}
运行结果如下:
Part2 不用strcat函数
- 如果不用函数的话,我们就要先找到s1字符串的末尾,我们只需要用一个循环查找就行,当i不满足条件时,那么s1[i]就指向了s1’\0’处
- 然后就可以把s2的字符串拷贝到s1处了,也就是一个一个字符的拷贝过去,知道遇到’\0’;
- 但是需要注意,s1拷贝完了之后后面是缺了一个’\0’的,所以我们要认为的加上去
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char s1[100] = { '0' };
char s2[50] = { '0' };
int i = 0;
int j = 0;
gets(s1);
gets(s2);
while (s1[i] != '\0')
{
i++;
}
while (s2[j] != '\0')
{
s1[i] = s2[j];
i++;
j++;
}
s1[i] = '\0';
printf("%s", s1);
return 0;
}
第七章 函数
7.1 最大公约数和最小公倍数
题目概述:
写两个函数,分别求两个整数的最大公约数和最小公倍数,用主函数调用这两个函数,并输出结果。两个整数由键盘输入。
题目思路:
最大公约数:辗转相除法
-
欧几里得算法是用来求两个正整数最大公约数的算法。古希腊数学家欧几里得在其著作《The Elements》中最早描述了这种算法,所以被命名为欧几里得算法。
扩展欧几里得算法可用于RSA加密等领域。
假如需要求 1997 和 615 两个正整数的最大公约数,用欧几里得算法,是这样进行的:
1997 ÷ 615 = 3 (余 152)
615 ÷ 152 = 4(余7)
152 ÷ 7 = 21(余5)
7 ÷ 5 = 1 (余2)
5 ÷ 2 = 2 (余1)
2 ÷ 1 = 2 (余0)
至此,最大公约数为1
以除数和余数反复做除法运算,当余数为 0 时,取当前算式除数为最大公约数,所以就得出了 1997 和 615 的最大公约数 1。
-
用C语言来表示就是
- 用x表示被除数、y表示除数、z表示余数
- z = x % y
- 除数y变成被除数x,余数z变成除数y
- x = y
- y = z
- 用x表示被除数、y表示除数、z表示余数
-
最小公倍数
- 两数相乘除以最大公约数
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int gcd(int x, int y)
{
int z = -1;
while (z != 0)
{
z = x % y;
x = y;
y = z;
}
return x;
}
int lcm(int x, int y)
{
int z = x * y / gcd(x, y);
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("最大公约数:%d\n", gcd(a, b));
printf("最小公倍数:%d\n", lcm(a, b));
return 0;
}
运行结果如下:
7.2 函数判断素数
题目概述:
写一个判断素数的函数,在主函数输入一个整数,输出是否素数的信息
题目思路:
- 判断一个数是否是素数,首先要知道素数是什么
- 素数:大于1的整数中,只能被1或者被自己整除的数
- 既然知道了怎么样的数是素数,我们就可以开始处理了
- 用i是否为素数
- 我们可以假设每个数都是素数,用flag来表示,1就是素数,0不是素数,最后返回flag
- 如果有一个数不能被2到n-1之间的数字整除,那么这个数就是素数
- 用i是否为素数
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Is_prime(int n)
{
int i = 0;
int flag = 1;
for (i = 2; i < n; i++)
{
if (n % i == 0)
{
flag = 0;
break;
}
}
return flag;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Is_prime(n);
if (ret == 1)
{
printf("是素数\n");
}
else
{
printf("不是素数\n");
}
return 0;
}
代码优化:
优化1:由于偶数都不是素数,我们可以只从奇数项开始,每个数加+2让下一个也是奇数
优化2:当一个数不是素数的时候,一定能下成下列的一个式子,且他是可以被一个因子整除的
例:m = a *b
16 = 4 * 4
16 = 2 * 8
a和b中一个数字是 <= sqrt(m) →[4]
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
int Is_prime(int n)
{
int i = 0;
int flag = 1;
for (i = 3; i <= sqrt(n); i+=2)
{
if (n % i == 0)
{
flag = 0;
break;
}
}
return flag;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Is_prime(n);
if (ret == 1)
{
printf("是素数\n");
}
else
{
printf("不是素数\n");
}
return 0;
}
运行结果如下:
7.3 行列互换
题目概述:
写一个函数,使给定的一个 3X3 的二维整型数组转置,即行列互换。
题目思路:
- 画一个对角线
对角线的值不变,然后让对角线外的对称值进行互换,既然是互换,那么就会要有一个中间变量temp进行互换
对应的下标如下:
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void Swap(int arr[3][3])
{
int i = 0;
int j = 0;
int temp = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < i; j++)
{
temp = arr[i][j];
arr[i][j] = arr[j][i];
arr[j][i] = temp;
}
}
}
int main()
{
int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
int i = 0;
int j = 0;
Swap(arr);
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
运行结果如下:
7.4 反序存放字符串
题目概述:
写一个函数,使输入的一个字符串按反序存放,如输入"CANADA",输出"ADANAC"。在主函数中输入和输出字符串。
题目思路:
- 本题只需要知道数组的最后一个元素即可
- 用最后的元素和最初的元素交换,如何访问到数组最后一个元素?可以用strlen函数
- 然后就好做了,但是char[sz]就是数组的最后一个元素吗?数组下标是从0开始的,所以ch[sz-1]才是真正的数组最后一个元素
- 由于循环完了一次i++,那么ch[1]就要跟ch[sz-1]的前一个元素交换顺序,但是我们应该如何交换呢?我们可以直接ch[sz-1-i],那么当i=0的时候,ch[0]就跟ch[5-1-0]交换,也就是ch[0] 和 ch[4],当i=1的时候,ch[1]和ch[5-1-1]交换,也就是ch[1]和ch[3]…以此类推
- 最后,由于是两两交换,所以我们不需要遍历整个数组,只要遍历半个就能完成交换,否则遍历完整个数组会把逆序的又逆回来,得到的还是CANADA
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
void reverse(char ch[100], int sz)
{
int i = 0;
int temp = 0;
for (i = 0; i < sz /2; i++)
{
temp = ch[i];
ch[i] = ch[sz - 1 - i];
ch[sz - 1 - i] = temp;
}
}
int main()
{
char ch[100] = { '0' };
gets(ch);
int sz = strlen(ch);
reverse(ch, sz);
printf("%s", ch);
return 0;
}
运行结果如下:
7.5 函数连接字符串
题目概述:
写一个函数,将两个字符串连接,如字符串 1 是"BEI",字符串 2 是"JING”,连接起来是"BEIJING"
题目思路:
- 我们要先找到s1字符串的末尾,用strlen计算字符串的长度,用sz保存,那么ch[sz]就是s1的’\0’位处
- 然后就可以把s2的字符串拷贝到s1处了,也就是一个一个字符的拷贝过去,知道遇到’\0’;
- 但是需要注意,s1拷贝完了之后后面是缺了一个’\0’的,所以我们要认为的加上去
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
void my_link(char s1[100], char s2[100], int sz)
{
int i = sz;
int j = 0;
while (s2[j] != '\0')
{
s1[i] = s2[j];
i++;
j++;
}
s1[i] = '\0';
}
int main()
{
char s1[100] = { '0' };
char s2[100] = { '0' };
gets(s1);
gets(s2);
int sz = strlen(s1);
my_link(s1, s2, sz);
printf("%s", s1);
return 0;
}
运行结果如下:
7.5 函数连接字符串
题目概述:
写一个函数,将两个字符串连接,如字符串 1 是"BEI",字符串 2 是"JING”,连接起来是"BEIJING"
题目思路:
- 我们要先找到s1字符串的末尾,用strlen计算字符串的长度,用sz保存,那么ch[sz]就是s1的’\0’位处
- 然后就可以把s2的字符串拷贝到s1处了,也就是一个一个字符的拷贝过去,知道遇到’\0’;
- 但是需要注意,s1拷贝完了之后后面是缺了一个’\0’的,所以我们要认为的加上去
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>
void my_link(char s1[100], char s2[100], int sz)
{
int i = sz;
int j = 0;
while (s2[j] != '\0')
{
s1[i] = s2[j];
i++;
j++;
}
s1[i] = '\0';
}
int main()
{
char s1[100] = { '0' };
char s2[100] = { '0' };
gets(s1);
gets(s2);
int sz = strlen(s1);
my_link(s1, s2, sz);
printf("%s", s1);
return 0;
}
运行结果如下:
7.6 元音字母复制字符串
题目概述:
写一个函数,将一个字符串中的元音字母复制到另一个字符串,然后输出。
题目思路:
- 首先要知道元音字母是什么:小写的a、e、i、o、u和大写的A、E、I、O、U
- 遍历字符串如果找到了元音字母就复制到新的字符串中即可
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void copy(char s1[], char s2[])
{
int i = 0;
int j = 0;
while (s1[i] != '\0')
{
if (s1[i] == 'A' || s1[i] == 'a'
|| s1[i] == 'E' || s1[i] == 'e'
|| s1[i] == 'I' || s1[i] == 'i'
|| s1[i] == 'O' || s1[i] == 'o'
|| s1[i] == 'U' || s1[i] == 'u')
{
s2[j] = s1[i];
j++;
//i++;可以放这不? 看下面
}
i++; //要在if外,否则当判断了s1[i]不是元音字母的时候,跳过了if语句的话就进入了死循环
}
s2[i] = '\0';
}
int main()
{
char s1[256] = { '0' }; //0-256都是有效字符
char s2[256] = { '0' };
gets(s1);
copy(s1, s2);
printf("%s", s2);
return 0;
}
运行结果如下:
7.7 数空数空
题目概述:
写一个函数,输人一个 4 位数字,要求输出这 4 个数字字符,但每两个数字间空个空格。如输入 2008,应输出"2 0 0 8"。
题目思路:
- 只需要输出一个字符在输出一个空格即可
- 但是题目要求若打印出2008,应该是“2 0 0 8“,很明显8后面是没有空格的,所以我们可以多加一个判断条件,用来判断下一个字符是不是’\0’,如果是的话我们就直接跳出循环。
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void space(char ch[])
{
int i = 0;
while (ch[i] != '\0')
{
printf("%c", ch[i]);
if (ch[i + 1] == '\0')
{
break;
}
printf(" ");
i++;
}
}
int main()
{
char ch[5] = { '0' };
scanf("%s", ch);
space(ch);
return 0;
}
7.8 函数统计字符个数
题目概述:
编写一个函数,由实参传来一个字符串,统计此字符串中字母、数字、空格和其他字符的个数,在主函数中输入字符串以及输出上述的结果
题目思路:
- 本题需要注意的只是变量的创建,如果要在函数里面记录个数的话是不可行的,因为在函数里面的参数是在整个函数的作用于里面,当出了这个函数之后这些变量就不存在了,为了让这些变量存在,我们可以利用全局变量
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int num = 0;
int letter = 0;
int space = 0;
int other = 0;
void total(char s1[])
{
int i = 0;
while (s1[i] != '\0')
{
if (s1[i] >= '0' && s1[i] <= '9')
num++;
else if ((s1[i] >= 'A' && s1[i] <= 'Z') || (s1[i] >= 'a' && s1[i] <= 'z'))
letter++;
else if (s1[i] == ' ')
space++;
else
other++;
i++;
}
}
int main()
{
char s1[256] = { '0'};
gets(s1);
total(s1);
printf("num = %d, letter = %d, space = %d, other = %d", num, letter, space, other);
return 0;
}
运行结果如下:
7.9 输出最长单词
题目概述:
写一个函数,输入一行字符,将此字符串中最长的单词输出
题目思路:
-
本题我们可以用i来表示单词的起始位置,j为单词的终止位置就是这样
-
初始位置
-
j到空格的位置
-
用j-i得出该单词的长度
-
假设this是最长的,将其存入新的字符数组中
-
this单词遍历完后,这个j就是下一轮循环的起始位置,要把j赋给i
-
-
一直循环直到字符串结束
-
每找到一个单词就比较一下存入的数组的长度,当新的这个单词比存入的数组的单词还要长,就要把他复制到字符数组s2中
-
计算字符串长度s2就需要用到strlen函数
-
拷贝字符串需要用到strncpy函数
-
关于这几个库函数的具体使用请看封面教材P183 6.5.2 对C的字符串函数的详细说明
-
-
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
void find_long(char s1[], char s2[])
{
int i = 0;
int j = 0;
int len = 0;
while (s1[i] != '\0')
{
j = i;
while (s1[j] != ' ' && s1[j] != '\0')
j++;
len = j - i;
if (len > strlen(s2))
{
strncpy(s2, s1 + i, len);
}
j++;
i = j;
}
}
int main()
{
char s1[256] = "This is a C language project";
char s2[256] = { '0' };
find_long(s1, s2);
printf("%s", s2);
return 0;
}
运行结果如下:
7.10 冒泡排序(起泡法)
题目概述:
写一个函数,用“起泡法”对输入的 10 个字符按由小到大顺序排列
题目思路:
起泡法就是冒泡排序
冒泡排序核心思想:相邻的元素进行比较
为方便演示,下图就用5个数字做演示
第一趟冒泡排序
第二趟冒泡排序
可以看到,5已经是最大的数字,固定在最后一位,所以我们在进行第二趟冒泡排序的时候,只需要对前四个数字进行排序即可
第三趟冒泡排序
可以看到,4和5的位置已经确定,只需要对前3个数字排序即可
第四趟冒泡排序
可以看到,3和4和5的位置已经确定,只需要对前2个数字进行排序即可
第四趟冒泡排序后,四个数字的位置已经确定,总共5个数字,进行了4趟排序,那么最后一个数字自然已经是排好序了
步骤:
1.确定趟数:有多少个数字,就要判断要交换多少趟,以上图为例,5个数字,只需要排序4趟,那么有n个数字,就需要进行n-1次的冒泡排序
2.确定每躺需要交换的次数:我们可以看到,每一趟冒泡排序之中,需要比较的次数都会减少。
3.如果出现了一趟都没有发生交换,那么我们可以用break提前跳出循环。
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void Sort(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
int flag = 1;
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
flag = 0;
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (flag == 1)
{
break;
}
}
}
int main()
{
int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
Sort(arr, sz);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果如下:
7.11 函数计算平均分
题目概述:
输入 10 个学生 5 门课的成绩,分别用函数实现下列功能:
1.计算每个学生平均分;
2.计算每门课的平均分;
3.找出所有 50 个分数中最高的分数所对应的学生和课程
题目思路:
- PS:由于本题数据有50个,输入太废劲…所以本题在初始化数组时已经把分数初始进去了
- 首先我们可以定义一个11*6的数组,第六列存放学生平均成绩,第11行存放课程成绩,然后依次遍历,把存放的值放入数组中
- 然后计算第三个最高成绩时候,由于要输出行和列,我们可以定义一个全局变量用来存放。因为要打印最高成绩,所以函数的返回类型是float类型,用一个float类型的变量来接收
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int row = 0;
int col = 0;
void ave_stu(float arr[][6]) //arr[][6]是算学生平均成绩的
{
int i = 0;
int j = 0;
float sum = 0.0;
for (i = 0; i < 10; i++)
{
sum = 0.0;
for (j = 0; j < 5; j++)
{
sum += arr[i][j];
arr[i][5] = sum / 5;
}
}
}
void ave_cor(float arr[][6])
{
int i = 0;
int j = 0;
float sum = 0.0;
for (j = 0; j < 5; j++)
{
sum = 0.0;
for (i = 0; i < 10; i++)
{
sum += arr[i][j];
}
arr[10][j] = sum / 10;
}
}
float find_high(float arr[][6])
{
int i = 0;
int j = 0;
float max = 0.0;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 5; j++)
{
if (max < arr[i][j])
{
max = arr[i][j];
row = i + 1;
col = j + 1;
}
}
}
return max;
}
int main()
{
float arr[11][6] = { {87,88,92,67,78},{88,86,87,98,90},{76,75,65,65,78},
{67,87,60,90,67},{77,78,85,64,56},{76,89,94,65,76},{78,75,64,67,77},
{77,76,56,87,85},{84,67,78,76,89},{86,75,64,69,90} };
int i = 0;
int j = 0;
ave_stu(arr);
ave_cor(arr);
float max = find_high(arr);
printf("每个学生的平均成绩是:");
for (i = 0; i < 10; i++)
{
printf("%.2f ", arr[i][5]);
}
printf("\n每个课程的平均成绩是:");
for (j = 0; j < 5; j++)
{
printf("%.2f ", arr[10][j]);
}
printf("\n最高成绩是:%.2f,学生号是:%d,课程号是:%d", max, row, col);
return 0;
}
7.12 不知道起什么题目名 X
题目概述:
写几个函数:
①输人 10 个职工的姓名和职工号
②按职工号由小到大顺序排序,姓名顺序也随之调整
③要求输入一个职工号,用折半查找法找出该职工的姓名,从主函数输人要查找的职工号,输出该职工姓名
代码实现:
本体代码实现是官方教材课后习题标准答案,非本人写
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#define N 10
int main()
{
void input(int[], char name[][8]);
void sort(int[], char name[][8]);
void search(int, int[], char name[][8]);
int num[N], number, flag = 1, c;
char name[N][8];
input(num, name);
sort(num, name);
while (flag == 1)
{
printf("\ninput number to look for:");
scanf("%d", &number);
search(number, num, name);
printf("continue or not(Y/N)?");
getchar();
c = getchar();
if (c == 'N' || c == 'n')
{
flag = 0;
}
}
return 0;
}
void input(int num[], char name[N][8])
{
int i;
for (i = 0; i < N; i++)
{
printf("input No.:");
scanf("%d", &num[i]);
printf("input name: ");
getchar();
gets(name[i]);
}
}
void sort(int num[], char name[N][8])
{
int i, j, min, templ;
char temp2[8];
for (i = 0; i < N - 1; i++)
{
min = i;
for (j = i; j < N; j++)
if (num[min] > num[j])
min = j;
templ = num[i];
strcpy(temp2, name[i]);
num[i] = num[min];
strcpy(name[i], name[min]);
num[min] = templ;
strcpy(name[min], temp2);
}
printf("\n result: \n");
for (i = 0; i < N; i++)
{
printf("\n %5d%10s", num[i], name[i]);
}
}
void search(int n, int num[], char name[N][8])//折半查找的函数
{
int top, bott, mid, loca, sign;
top = 0;
bott = N - 1;
loca = 0;
sign = 1;
if ((n < num[0]) || (n > num[N - 1]))
loca = -1;
while ((sign == 1) && (top <= bott))
{
mid = (bott + top) / 2;
if (n == num[mid])
{
loca = mid;
printf("NO.%d,his name is %s.\n", n, name[loca]);
sign = -1;
}
else if (n < num[mid])
bott = mid - 1;
else
top = mid + 1;
}
if (sign == 1 || loca == -1)
printf("%d not been found.\n", n);
}
7.13 递归返回最大值
题目概述:
输入 4 个整数,找出其中最大的数。用函数的递归调用来处理
题目思路:
- 本题要明白递归是什么,递归递归,是先递推再回归,如图所示,假设本题a,b,c,d输入值为1,2,3,4
- 图解
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Max(int x, int y)
{
if (x > y)
return x;
else
return y;
}
int findmax(int a, int b, int c, int d)
{
int max = Max(Max(Max(a, b),c),d);
return max;
}
int main()
{
int a = 0;
int b = 0;
int c = 0;
int d = 0;
scanf("%d %d %d %d", &a, &b, &c, &d);
int max = findmax(a, b, c, d);
printf("%d", max);
return 0;
}
7.14 整数转换字符串
题目概述:
用递归法将一个整数转换成字符串。例如,输入 483,应输出字符串"483",n的位数不确定,可以是任意位数的整数。
题目思路:
画图理解:
红色线是递推,绿色线是回归,递归递归先递推后回归
- 整数+字符变成字符
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void change(int n)
{
if (n / 10 != 0)
{
change(n / 10);
}
printf("%c", n % 10 + '0');
}
int main()
{
int num = 0;
scanf("%d", &num);
change(num);
return 0;
}
7.15 计算该日是该年的第n天
题目概述:
给出年、月、日,计算该日是该年的第 n 天
题目思路:
- 该年的第几天,只需要把月和日加起来就好了,但是有个问题,因为年有闰年,而闰年是29天,所以我们还要多判断一个闰年便于计算
- 我们可以专门
- 写一个函数专门判断闰年,
- 写一个函数专门计算该年的月份有多少天,而这个函数我们存月份的天数的时候,可以用数组存放可以建立一个长度为13的数组,下标为0的部分存放29,也就是闰年的二月,那么从下标为1开始就是对应的1-12月。
- 写一个函数专门计算年月日(年月函数+日)
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int is_leap_year(int year)
{
if ((year % 4 == 0 && year / 100 != 0) || (year % 4 == 0))
return 1;
return 0;
}
int cal_year_month(int year, int month)
{
int arr[13] = { 29,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && is_leap_year(year))
return arr[0];
return arr[month];
}
int cal_year_month_day(int year, int month, int day)
{
int i = 0;
int days = 0;
for (i = 1; i < month; i++)
{
days += cal_year_month(year, i);
}
days += day;
return days;
}
int main()
{
int year = 0;
int month = 0;
int day = 0;
scanf("%d %d %d", &year, &month, &day);
int days = cal_year_month_day(year, month, day);
printf("%d年%d月%d日是该年的第%d天", year, month, day, days);
return 0;
}
运行结果如下:
随便找个工具验证:
第八章课后习题
8.1 指针由小到大的顺序输出
题目概述:
输入 3 个整数,按由小到大的顺序输出
题目思路:
本题只需要两两比较两个数的大小,需要用指针来完成,我们定义三个指针变量,存放三个变量的地址,用指针解引用之后比较然后排序即可
比较的思路是:
- 两两进行比较,
- 先让pmin和pmid进行比较,把较小的值的地址存放到pmin中
- 再让pmin和pmax进行比较,把较小的值的地址还是存放在pmin中
- 此时已经比较完最小值
- 然后再让pmid和pmax进行比较,把次小的值存放到pmid中
- 之所以直接改变指针指向,是因为只改变指针指向不需要进行传值,这样效率更高,这个优势会在第二题有所体现
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
int c = 0;
int* pmin = &a;
int* pmid = &b;
int* pmax = &c;
int* temp = NULL;
scanf("%d %d %d", &a, &b, &c);
if (*pmin > *pmid)
{
temp = pmin;
pmin = pmid;
pmid = temp;
}
if (*pmin > *pmax)
{
temp = pmin;
pmin = pmax;
pmax = temp;
}
if (*pmid > *pmax)
{
temp = pmid;
pmid = pmax;
pmax = temp;
}
printf("%d %d %d", *pmin, *pmid, *pmax);
return 0;
}
运行结果如下:
8.2 输入 3 个字符串,按由小到大的顺序输出
题目概述:
输入 3 个字符串,按由小到大的顺序输出
题目思路:
本题是按照首字符大小进行排序输出
- 做这题首先要回顾两种字符函数
- strcmp函数
- 函数原型:int strcmp(char * s1, char *s2)
- 返回:s1如果比s2大则返回大于0的数,相等返回等于0,s1如果比s2小则返回小于0的数
- 注意,比较的是首字符的大小
- strcmp函数
- 做法与8.1类似
- 首先要明白一点,字符串的本质就是字符数组,所以我们直接定义一个字符数组用来存放字符串
- 因为字符串不能直接进行比较,所以我们要用一个strcmp来进行两两比较
- 先让pmin和pmid进行比较,把较小的值的地址存放到pmin中
- 再让pmin和pmax进行比较,把较小的值的地址还是存放在pmin中
- 此时已经比较完最小值
- 然后再让pmid和pmax进行比较,把次小的值存放到pmid中
- 直接改变指针的指向的好处是,如果字符串很长很长,如果用中间变量的方法来进行交换的话,这样效率就特别低了,但是如果值改变指针的指向,是不需要交换的,效率自然高了
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char s1[100] = { 0 };
char s2[100] = { 0 };
char s3[100] = { 0 };
gets(s1);
gets(s2);
gets(s3);
char* temp = NULL;
char* pmin = s1;
char* pmid = s2;
char* pmax = s3;
if (strcmp(pmin, pmid) > 0)
{
temp = pmin;
pmin = pmid;
pmid = temp;
}
if (strcmp(pmin, pmax) > 0)
{
temp = pmin;
pmin = pmax;
pmax = temp;
}
if (strcmp(pmid, pmax) > 0)
{
temp = pmid;
pmid = pmax;
pmax = temp;
}
printf("%s\n", pmin);
printf("%s\n", pmid);
printf("%s\n", pmax);
return 0;
}
运行结果如下:
8.3 整数互换
题目概述:
输入 10 个整数,将其中最小的数与第一个数对换,把最大的数与最后一个数对换。写3个函数:
(1) 输人 10 个数;
(2) 进行处理;
(3)输出 10 个数
题目思路:
-
首先先处理输入函数
- 只需要注意,数组名(arr)就是首元素(&arr[0])的地址,地址的加减是以指针指向的元素为大小进行偏移,比如说arr+1,表示的是数组第一个元素的地址,等价于&arr[1]
- 我们在输入的时候,因为数组名自己就是地址,所以我们不需要再加&
-
然后优先处理输出函数(因为最后的最难,放在最后解决)
- 只需要注意,因为输出的是一个整数,而arr+i是地址,是整型指针,所以我们需要解引用,才能访问到指针指向的那个值
-
最后处理交换
-
题目要求其中最小的数与第一个数对换,把最大的数与最后一个数对换
- 只需要定义一个pmin的指针变量和pmax的指针变量,并将他们初始化为首元素的地址,那么让他们一 一进行比较,循环结束后,pmin指针所指的就是最小的数,pmax指针所指的就是最大的数
- 然后进行互换,定义一个中间变量temp,要交换值就要解引用pmin和pmax得到他们的值
- 然后一 一互换即可
-
但是可能会出现一个问题,假如首元素地址就是最大值,即max就是arr的时候,那么在进行最小值的互换时,会把max的值给互换掉,此时max指向的arr[0]就不是最大值了,所以会导致最大值没有成功排序。如下图
-
进行pmin的交换后如下图:
-
然后再进行pmax交换
最终的运行结果为: -
解决办法:将pmin传给pmax,如下图
-
执行完pmin和第一个数交换后,就会把最大值交换到pmin和pmax所指向的地址
- 这样子就能把pmax进行交换了,不会发生错误
-
-
-
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void input(int* arr, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
scanf("%d", (arr + i));
}
}
void handle(int* arr, int sz)
{
int i = 0;
int* pmin = arr;
int* pmax = arr;
for (i = 0; i < sz; i++)
{
if (*(arr + i) < *pmin)
{
pmin = arr + i;
}
if (*(arr + i) > *pmax)
{
pmax = arr + i;
}
}
if (pmax == arr)
{
pmax = pmin;
}
int temp = 0;
temp = *pmin;
*pmin = *arr;
*arr = temp;
temp = *pmax;
*pmax = *(arr + sz - 1);
*(arr + sz - 1) = temp;
}
void output(int* arr, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(arr + i));
}
}
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
input(arr, sz);
handle(arr, sz);
output(arr, sz);
return 0;
}
运行结果如下:
8.4 数组旋转
题目概述:
有 n 个整数,使前面各数顺序向后移 m 个位置,最后 m 个数变成最前面m 个数,见图 8.28。写一函数实现以上功能,在主函数中输入 n个整数和输出调整后的 n 个数。
题目思路:
-
这道题我们可以理解为是数组的旋转,那么他是如何旋转的呢?
-
先将末尾的数据,用一个中间变量保存起来,ps:注意下面的不是新数组,是同一个数组,只是为了方便理解拆开了两个,实际上是同一个数组
-
然后从后往前,将数据逐个向后移动一个位置,移动完如下
-
将中间变量存放的值再放到起始位置,就完成了一次数组旋转
-
我们可以发现,数组旋转完了一次数据后,9就在数组前面了,那么是不是可以理解为,只要把数组旋转m次,每次旋转一个数据,就能得到m个数在前面。所以我们只需要写一个循环,让她循环m次,然后在内层循环每次旋转一次数据即可。
-
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void move(int* arr, int n, int m)
{
int i = 0;
int j = 0;
int temp = 0;
for (i = 0; i < m; i++)
{
temp = *(arr + n - 1);
for (j = n - 1; j > 0; j--)
{
*(arr + j) = *(arr + j - 1);
}
*arr = temp;
}
}
int main()
{
int arr[50] = { 0 };
int n = 0;
int i = 0;
printf("请输入n:");
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%d", arr + i);
}
int m = 0;
printf("请输入m:");
scanf("%d", &m);
move(arr, n, m);
for (i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果如下:
8.5 1 2 3游戏
题目概述:
有 n 个学生围成一圈,顺序排号。从第 1 个学生开始报数(从 1到 3 报数),凡报到 3 的学生退出圈子,到最后只留下一名学生,问最后留下的是原来第几号学生
题目思路:
- 定义一个很大的数组,输入n就代表总人数
- 如何用变量m表示出局人数,而游戏开始时m就是n的人数,当m=1时游戏结束,所以循环条件给出m>1
- 内部循环,开始报数
- 从1-3开始一直报数,当有人是3的时候重新开始从1报数,此时此人已经出局m–
- 然后一直循环,要判断是否有人是3,3的人已经被淘汰,用continue直接跳过后面的语句
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[100] = { 0 };
int n = 0;//总人数
int i = 0;
int num = 1;//喊的数字,从1开始
printf("请输入要玩的人数:>");
scanf("%d", &n);
int m = n;//剩余人数
while (m > 1)
{
for (i = 0; i < n; i++)
{
if (*(arr + i) == 3)
{
continue;
}
*(arr + i) = num;
if (num == 3)
{
num = 0;//后面还有个语句++
m--;
}
num++;
}
}
for (i = 0; i < n; i++)
{
if (*(arr + i) != 3)
{
printf("%d", i + 1);//第几号人
break;
}
}
return 0;
}
运行结果如下:
8.6 求一个字符串的长度
题目概述:
编写一函数,求一个字符串的长度。在 main 函数中输入字符串,并输出其长度。
题目思路:
- 本题只需要知道字符串的结束标志是’\0’,构建函数后,我们只需要用指针就能指向字符数组中的元素,只要他='\0’那么就结束了
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int length(char* ch)
{
int i = 0;
int count = 0;
for (i = 0; *(ch + i) != '\0'; i++)
{
count++;
}
return count;
}
int main()
{
char ch[256] = { 0 };
gets(ch);
int len = length(ch);
printf("%d", len);
return 0;
}
运行结果如下:
8.7 字符串复制
题目概述:
有一个字符串 a,内容为"My name is Li jilin,“,另有一字符串 b,内容为"Mr.Zhang Haoling is very happy.”。
写一函数,将字符串 b 中从第 5 个到第 17 个字符(即"Zhang Haoling")复制到字符串 b 中,取代字符串 a 中第 12 个字符以后的字符(即"Lijilin.")。输出新的字符串 a。
题目思路:
- 本题用指针非常好做,构造函数后,他们各自指向到要复制的地方,然后一个一个字符复制,直到复制结束
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void copystr(char* stra, char* strb)
{
int m = 11;
int n1 = 4;
int n2 = 16; //定义这两个变量的好处是在循环时比较
char* pa = stra + m; //数组下标从0开始,所以+11,指向a中第12个字符
char* pb = strb + n1; //+4,指向b中第5个字符
while (n1 <= n2)
{
*pa = *pb;
pa++;
pb++;
n1++;
}
*pa = '\0';
}
int main()
{
char stra[50] = "My name is Li jilin.";
char strb[50] = "Mr. Zhang Haoling is very happy.";
copystr(stra, strb);
printf("%s\n", stra);
return 0;
}
运行结果如下:
8.8 统计字符
题目概述:
输入一行文字,找出其中大写字母、小写字母、空格、数字以及其他字符各有多少?
题目思路:
- 用指针判断即可
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
/*输入一行文字,找出其中大写字母、小写字母、空格、数字以及其他字符各有多少?*/
#include <stdio.h>
int main()
{
char ch[256] = { 0 };
int i = 0;
int big = 0;
int little = 0;
int space = 0;
int num = 0;
int other = 0;
gets(ch);
for (i = 0; *(ch + i) != '\0'; i++)
{
if (*(ch + i) >= 'A' && *(ch + i) <= 'Z')
{
big++;
}
else if (*(ch + i) >= 'a' && *(ch + i) <= 'z')
{
little++;
}
else if (*(ch + i) == ' ')
{
space++;
}
else if (*(ch + i) >= '0' && *(ch + i) <= '9')
{
num++;
}
else
{
other++;
}
}
printf("大写字母:%d\n", big);
printf("小写字母:%d\n", little);
printf(" 空格 :%d\n", space);
printf(" 数字 :%d\n", num);
printf("其他字符:%d\n", other);
return 0;
}
运行结果如下:
8.9 指针实现字符串排序
题目概述:
在主函数中输入10 个等长的字符串。用另一个函数对它们排序。然后在主函数中输出这 10 个已排好序的字符串
题目思路:
-
整体思路:
- 首先定义一个二维数组,用来存放10个字符串,然后在函数题里面进行排序,排序的方法用的是冒泡排序
-
如果用利用指针去做本题,还需知什么是指针数组以及关于操作字符串的字符串函数
-
指针数组
-
首先先来复习以下二维数组,我们知道,二维数组是存放一维数组的数组,那么我们就可以用指针数组来模拟实现二维数组
-
首先先了解指针数组的定义
int (*p)[10];
这个是什么意思呢?
对int (*p)[10]一个一个拆解
p
:p是数组指针变量名,前面带个*,代表p是一个指针
-
[10]
:代表p指向数组的元素个数为10,即p指向数组的元素个数 -
int
:代表p指向的数组元素类型是整型,即p指向的数组的元素类型
注意:为什么不能写成
int *p[10]
,而要写成int (*p)[10]
呢?因为:[]的优先级比*高,那么p会先和[]结合,此时p[]就是一个数组,此时p就是数组名,数组的元素类型是int * 类型,他是一个存放指针的数组,叫做数组指针。
而int (*p)[10]
,此时()优先级最高,那么(*p)
代表p是一个指针,(*p)[10]
证明p指向的数组的元素个数为10,int (*p)[10]
代表p指向的的是一个大小为10的整型数组类似于这样。
-
了解了这个,我们就能用指针数组来实现二维数组了
- 我们知道,数组名表示首元素的地址,那么arr是不是就是二维数组的首元素地址呢?
答案是正确的,arr表示首元素地址,我们刚刚讲过,二维数组是存放一维数组的数组,所以arr表示首元素地址,就是第一行一维数组的地址,存放一维数组的地址,我们用指针来存放,既然是数组,我们就用数组指针来存放,一维数组的类型是int [5],那么数组指针类型就是int (*) [5]。这样我们就得到了二维数组传参的本质:二维数组传参本质上传递是地址,这个地址是一维数组的地址,也就是首元素的地址,我们可以将二维数组写成指针的形式,便于我们理解
- 我们知道,数组名表示首元素的地址,那么arr是不是就是二维数组的首元素地址呢?
-
-
字符串函数
- strcmp函数
- 函数原型:int strcmp(char * s1, char *s2)
- 返回:s1如果比s2大则返回大于0的数,相等返回等于0,s1如果比s2小则返回小于0的数
- 注意,比较的是首字符的大小
- strcpy函数
- 函数原型:char *strcpy(char *dst, const char *src);
- 函数功能:把src字符串复制到dst字符串,需要指定目标数组和源字符串
- 函数返回:该函数返回一个指向最终的目标字符串 dest 的指针。
- strcmp函数
-
最后用冒泡排序进行排序即可完成本题
冒泡排序核心思想:相邻的元素进行比较
为方便演示,下图就用5个数字做演示,字符串思路一样,只是交换的方法不一样,要用到字符串函数操作第一趟冒泡排序
第二趟冒泡排序
可以看到,5已经是最大的数字,固定在最后一位,所以我们在进行第二趟冒泡排序的时候,只需要对前四个数字进行排序即可第三趟冒泡排序
可以看到,4和5的位置已经确定,只需要对前3个数字排序即可第四趟冒泡排序
可以看到,3和4和5的位置已经确定,只需要对前2个数字进行排序即可
第四趟冒泡排序后,四个数字的位置已经确定,总共5个数字,进行了4趟排序,那么最后一个数字自然已经是排好序了
步骤:
1.确定趟数:有多少个数字,就要判断要交换多少趟,以上图为例,5个数字,只需要排序4趟,那么有n个数字,就需要进行n-1次的冒泡排序
2.确定每躺需要交换的次数:我们可以看到,每一趟冒泡排序之中,需要比较的次数都会减少。
3.如果出现了一趟都没有发生交换,那么我们可以用break提前跳出循环。
-
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
void str_sort(char(*p)[40], int row)
{
int i = 0;
int j = 0;
char temp[40] = { 0 };
int flag = 1;
for (i = 0; i < 10 - 1; i++)
{
for (j = 0; j < 10 - 1 - i; j++)
{
if (strcmp(*(p + j), *(p + j + 1)) > 0)
{
flag = 0;
strcpy(temp, *(p + j));
strcpy(*(p + j), *(p + j + 1));
strcpy(*(p + j + 1), temp);
}
}
if (flag == 1)
{
break;
}
}
}
int main()
{
char str[10][40] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)
{
gets(*(str + i));
}
str_sort(str, 10);
printf("--------以下是排序后--------\n");
for (i = 0; i < 10; i++)
{
printf("%s\n", *(str + i));
}
return 0;
}
运行结果如下:
8.10 逆序输出整数
题目概述:
将 n 个数按输入时顺序的逆序排列,用函数实现
题目思路:
- 本题并不复杂,只需要定义一个left和right的指针,一个指向最前面,一个指向最后面,让他们两两交换即可
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void reverse(int* arr, int n)
{
int* left = arr;
int* right = arr + n - 1;
int temp = 0;
while (left <= right)
{
temp = *left;
*left = *right;
*right = temp;
left++;
right--;
}
}
int main()
{
int arr[100] = { 0 };
int n = 0;
int i = 0;
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%d", arr + i);
}
reverse(arr, n);
for (i = 0; i < n; i++)
{
printf("%d ", *(arr + i));
}
return 0;
}
运行结果如下:
8.11 逆序存放整数
题目概述:
编写一个函数 inv,将数组 a 中 n 个整数按相反顺序存放,用指针变量作为调用该函数时的实参。
题目思路:
- 本题跟上题的意思难道不是一样吗??还是我理解错误了
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void reverse(int* arr, int n)
{
int* left = arr;
int* right = arr + n - 1;
int temp = 0;
int i = 0;
while (left <= right)
{
temp = *left;
*left = *right;
*right = temp;
left++;
right--;
}
}
int main()
{
int arr[100] = { 0 };
int n = 0;
int i = 0;
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%d", arr + i);
}
reverse(arr, n);
for (i = 0; i < n; i++)
{
printf("%d ", *(arr + i));
}
return 0;
}
终于完成指针的习题了,太难了。。。
8.12 输出字符串中的整数
题目概述:
8.12 输人一个字符串,内有数字和非数字字符,例如:
a123x456 17960? 302tab5876
将其中连续的数字作为一个整数,依次存放到一数组 a 中。例如,123 放在 a[0],456 放在a[1]…共有多少个整数,并输出这此数。
题目思路:
- 总体思路:要先定义一个字符数组来接收,然后一个再定义一个整型数组,用来存放字符串中的整数
- 循环判断到字符串结束
- 判断str+i所指向的字符是不是数字字符,如果是的话,j++,j此时记录的是字符串中有多少个整数字符
- 当str+i所指向的字符不是数字字符,是其他字符,那么就要开始计算一串数字字符,并把他变为整数
- 首先整数有个位十位百位,所以要循环,让e代表该位数所乘的位阶,然后把他存到digit
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
char str[50] = { 0 };
int a[10] = { 0 };
int i = 0;
int j = 0;
int k = 0; //位数
int digit = 0; //位数相加的整数
int e = 0; //位数要乘的位阶
int n = 0; //记录有多少整数
int* pa = a; //指向数组a的指针
gets(str);
while (*(str + i) != '\0')
{
if (*(str + i) >= '0' && *(str + i) <= '9')
{
j++; //如果字符串中的字符是整型字符的话,记录有多少个整型字符
}
else //如果字符串不是整型字符,开始把记录多少位数的整型字符变成整数
{
if (j > 0)
{
e = 1;
digit = 0;
for (k = 0; k < j; k++)
{
digit += (*(str + i - 1 - k) - '0') * e;
e = e * 10;
}
*pa = digit;
pa++;
n++;
j = 0;
}
}
i++;
}
if (j > 0)
{
e = 1;
digit = 0;
for (k = 0; k < j; k++)
{
digit += (*(str + i - 1 - k) - '0') * e;
e = e * 10;
}
*pa = digit;
pa++;
n++;
j = 0;
}
printf("该字符串中一共有多少%d个整数\n", n);
for (i = 0; i < n; i++)
{
printf("%d ", *(a + i));
}
return 0;
}
运行结果如下:
本题核心代码就是这个,下面进行讲解
while (*(str + i) != '\0')
{
if (*(str + i) >= '0' && *(str + i) <= '9')
{
j++; //如果字符串中的字符是整型字符的话,记录有多少个整型字符
}
else //如果字符串不是整型字符,开始把记录多少位数的整型字符变成整数
{
if (j > 0)
{
e = 1;
digit = 0;
for (k = 0; k < j; k++)
{
digit += (*(str + i - 1 - k) - '0') * e;
e = e * 10;
}
*pa = digit;
pa++;
n++;
j = 0;
}
}
i++;
}
从else语句开始分析
-
首先是为什么要-‘0’,是因为在字符串里面的数字是数字字符,所以我们要-'0’使他变成整数
-
而为什么是
(*(str + i - 1 - k) - '0')
-
原因是:在循环体里面我们有一个if语句的判断,当if语句不是数字字符的时候,他就会走else语句,也就是说,当他走else语句的时候,他就不是数字字符了,而是其他字符,其实指针指向的是这里
-
也就是’x’的地址,所以,在我们计算位数的时候需要-1,使他指到’3’处
-
但是,由于我们计算的是位数相加,所以我们要-k,当k=0时,str+i-1-k就是’3’的地址
-
当k=1时,str+i-1-k就是’2’的地址,此时e是10,所以得出2 * 100=200
-
当k=2时,str+i-1-k就是‘1’的地址,此时e是100,所以得出1*100 = 100
-
当k=3时,不满足循环条件,跳出循环
-
-
-
-
然后用一个pa的指针指向数组a,把数组存在此处,pa++
-
n++,记录这个字符串有多少个整数
-
循环结束
循环结束后,你会发现循环体内的语句我又重复了一遍,这是为什么呢?
-
那是因为防止末尾的字符串是数字,无法复制,为什么无法复制?
- 因为循环判断,当*(str+i)指向的是’\0’,不满足循环条件跳出循环,而跳出循环之后,对末尾的数字就无法操作了,所以在跳出循环之后需要再操作一次
8.13 矩阵转置
题目概述:
题目思路:
- 画一个对角线
对角线的值不变,然后让对角线外的对称值进行互换,既然是互换,那么就会要有一个中间变量temp进行互换
对应的下标如下:
-
本题的难点就是如何用指针表示二维数组,从而进行行列互换
-
现在我们就来学习如何用指针来表示二维数组,我们要知道arr[i]是等价于*(arr +i)的,所以二维数组的表示就是
arr[i][j] <==> *(arr + i)[j] <==> *(*(arr + i) + j)
-
代码实现:
我们可以先写成二维数组的形式,再用上述的转换变成指针表示
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define ROW 3
#define COL 3
void swap(int(*arr)[3], int row, int col)
{
int temp = 0;
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < i; j++) //为什么要小于i?因为j小于col的话会把他换回去,也就是实现了两次互换
{
temp = arr[i][j];
arr[i][j] = arr[j][i];
arr[j][i] = temp;
}
}
}
int main()
{
int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
int i = 0;
int j = 0;
int row = ROW;
int col = COL;
swap(arr, row, col);
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
指针表示:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define ROW 3
#define COL 3
void swap(int(*arr)[3], int row, int col)
{
int temp = 0;
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < i; j++) //为什么要小于i?因为j小于col的话会把他换回去,也就是实现了两次互换
{
temp = *(*(arr + i) + j);
*(*(arr + i) + j) = *(*(arr + j) + i);
*(*(arr + j) + i) = temp;
}
}
}
int main()
{
int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
int i = 0;
int j = 0;
int row = ROW;
int col = COL;
swap(arr, row, col);
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf("%d ", *(*(arr + i) + j));
}
printf("\n");
}
return 0;
}
运行结果如下: