C程序设计-方法与实践(清华大学出版社)习题解析

1. 前言

本习题解析只是作为一种参考,代码不唯一!
补充:书本重要例题点击跳转
补充: 江西财经大学研究生2016-2022年真题解析及归纳总结
补充:感谢大家一路以来对本文的支持!由于作者本人现在已经考完研了,准备找工作了,开始回去学习我的主修课程JAVA了,由于时间问题,可能接下来不会更新本栏目的题目了,还请大家多多谅解,也欢迎一些热心研友能够对一些题目在评论区解答,或者将解答私聊发我,我来为大家及时更新,再次感谢大家的支持,祝大家考研成功!学习顺利!

2. C语言概述

1)习题 2.3

编写程序,由键盘输入任意3个数,找出其中最小的数

#include <stdio.h>
int main() {
	int x, y, z,min;
	printf("请输入第1个数:");
	scanf("%d", &x);
	printf("请输入第2个数:");
	scanf("%d", &y);
	printf("请输入第3个数:");
	scanf("%d", &z);
	if (x < y){
		if (x < z){
			min = x;
		}
		else{
			min = z;
		}
	}
	else{
		if (y > z){
			min = z;
		}
		else {
			min = y;
		}
	}
	printf("3个数的最小值是:%d", min);
	
	return 0;
}

总结:
本质是打擂台!

2)习题 2.4

编写程序,从键盘输入两个整数分别给变量x、y,如果x>y,则输出x及x-y的值;否则,输出y及y-x的值。

#include <stdio.h>
int main(){
	int x, y;
	printf("请输入x:");
	scanf("%d", &x);
	printf("请输入y:");
	scanf("%d", &y);
	if (x > y) {
		printf("x的值为:%d\nx-y的值为:%d", x,x-y);
	}
	else
	{
		printf("y的值为:%d\ny-x的值为:%d", y, y-x);
	}
	return 0;
}

3)习题 2.5

编写程序,求1+3+5+……+99。

#include <stdio.h>
int main() {
	int sum = 0;
	for (int i = 1; i < 100; i=i+2)
	{
		sum += i;
	}
	printf("1+3+5+…+99的和为:%d", sum);
	return 0;
}

4)习题 2.6

编写程序,统计1000以内的自然数中3的倍数之和。

#include<stdio.h>
int main() {
	int sum = 0,j = 1;
	for (int i = 3; i < 1000; i = 3 * j)
	{
		//printf("i为:%d\n", i);
		sum += i;
		j++;
	}
	printf("1000以内的自然数中3的倍数之和为:%d", sum);
	return 0;
}

5)习题 2.7

编写程序,输出0°~360°中所有度数为5°倍数的角度的正弦值和余弦值,即输出0°、5°、10°、15°、……、360°的正弦值和余弦值。

备注:
使用正余弦函数需要添加头文件math.h
正余弦函数(参数只能接受弧度制)

  1. double sin(double x);
  2. double cos(double x);

反余弦函数(参数值为-1-1)

  1. double acos(double x); 返回以弧度制表示的x的反余弦
#include<stdio.h>
#include<math.h>
int main() {
    /*
    int n;
    double pi = acos(-1);//以弧度表示的 x 的反余弦
    printf("弧度:%f", pi);//3.1415……
    scanf("%d", &n);//度数
    printf("%f %f\n", sin(n / 180.0 * pi), cos(n / 180.0 * pi));//sin和cos内必须使用弧度制;
    */
    int degree = 0;
    double Pi = acos(-1);
    for (int i = 0; degree<= 360; i++)
    {
        degree = 5 * i;
        double y = sin(Pi / 180.0 * degree);
        double x = cos(Pi / 180.0 * degree);
        printf("%d度的正弦值为:%f,余弦值为:%f\n",degree,y,x);
    }
	return 0;
}

6)习题 2.8

编写程序,由键盘输入20个整数,统计其中的正整数、负整数、0分别有多少个?并分别计算其中的正整数、负整数之和以及各自的平均值(结果为浮点型,输出时保留2位小数)。

补充:
1.保留2位小数,使用点‘’.”+数字 例如:%.2f(保留2位小数)
2.默认右对齐

#include <stdio.h>
int main() {
	int posNum = 0, negNum = 0 , zero = 0;
	double posSum = 0, negSum = 0;
	//double posAver, negAver = 0;
	int num;
	for (int i = 0; i < 20; i++)
	{
		printf("请输入整数:");
		scanf("%d", &num);
		if (num > 0)
		{
			posNum++;
			posSum += num;
		}
		else if (num < 0)
		{
			negNum++;
			negSum += num;
		}
		else
		{
			zero++;
		}
	}
	printf("正整数有:%d个,负整数有:%d个,0有:%d个\n",posNum,negNum,zero);
	printf("正整数之和为:%.2f,负整数之和为:%.2f\n", posSum, negSum);
	printf("正整数的平均值为:%.2f,负整数的平均值为:%.2f", posSum / posNum, negSum / negNum);

	return 0;
}

7)习题 2.9

编写程序,由键盘输入20个整数,分别找出其中的最大正整数、最小正整数、最大负整数、最小负整数

#include <stdio.h>
int main() {
	int data[20];
	int num;
	int posAppear = 0, negAppear = 0;
	int posMax, posMin, negMax, negMin;
	for (int i = 0; i < 20; i++)
	{
		printf("请输入第%d个数:", i + 1);
		scanf("%d", &num);
		data[i] = num;
	}
	for (int j = 0; j < 20; j++)
	{
		if (data[j] > 0) {
			posAppear++;
			if (posAppear == 1)
			{
				posMax = posMin = data[j];
			}
			else
			{
				posMax = posMax > data[j] ? posMax : data[j];
				posMin = posMin < data[j] ? posMin : data[j];
			}
		}
		else if (data[j] < 0)
		{
			negAppear++;
			if (negAppear == 1)
			{
				negMax = negMin = data[j];
			}
			else
			{
				negMax = negMax > data[j] ? negMax : data[j];
				negMin = negMin < data[j] ? negMin : data[j];
			}
		}
		else
		{
			continue;
		}
	}
	if (posAppear != 0 && negAppear != 0) //存在整数也存在负数
	{
		printf("最大正整数为:%d,最小正整数为:%d\n", posMax, posMin);
		printf("最大负整数为:%d,最小负整数为:%d\n", negMax, negMin);
	}
	else
	{
		if (posAppear == 0 && negAppear == 0)
		{
			printf("输入全为0,不存在最大最小正负整数!");
			
		}
		else if (negAppear == 0)
		{
			printf("最大正整数为:%d,最小正整数为:%d\n", posMax, posMin);
			printf("不存在最大、最小负整数!");
		}
		else
		{
			printf("不存在最大、最小正整数!");
			printf("最大负整数为:%d,最小负整数为:%d\n", negMin, negMin);
		}
	}
	return 0;
}

总结:
使用数组完成打擂台,数组是个好工具
数组的创建:int 数组名[长度];
数组不初始化,默认的值是不确定的
全初始化:例 int data[10] = {0},默认值都为0

补充)习题 2.1

在这里插入图片描述

#include<stdio.h>
int main() {
	int x, y;
	printf("请输入x的值:");
	scanf("%d", &x);
	if (x < 1)
	{
		y = x;
	}
	else if (x >= 1 && x < 10)
	{
		y = 2*x - 1;
	}
	else
	{
		y = 3 * x + 11;
	}
	printf("y的值为:%d", y);
	return 0;
}

3. 数据类型与输入输出

1)习题 3.3 程序阅读

(1)若x为int型变量,则执行以下语句后的输出结果是什么?

#include <stdio.h>
int main() {
	int x = 0xDEF;
	printf("%4d,%4o,%4x", x, x, x);
	return 0;
}

结果:在这里插入图片描述

注意:
1.DEF输出后是小写的
2.%o代表8进制,%x代表16进制
3.记忆:o是October,x是sixteen,d是decimal

(2)若x、y为int型变量,则执行以下语句后的输出结果是什么?

#include <stdio.h>
int main() {
	int x, y;
	x = 015,y = 0x15;
	printf("%4o%4x\n",x,y);
	printf("%4x%4d\n",x,y);
	printf("%4d%4o\n",x,y);
	return 0;
}

结果:
在这里插入图片描述

补充:
1.0开头代表八进制,0x开头代表16进制
2.程序默认右对齐
3.左对齐可以使用“-”,例如:%-4f


以上两题主要考察进制的转化,要熟悉了解进制的转化规则:
1.二进制转十进制,主要是乘2的几次幂再相加
2.十进制转二进制:除2取余法
3.二进制转八进制:取三合一法(反之亦然)
4.二进制转十六进制:取四合一法(反之亦然)

(3)执行以下语句后的输出结果是什么

#include <stdio.h>
int main() {
	char c1 = 'a', c2 = 'b', c3 = 'c', c4 = '\101', c5 = '\116';
	printf("abc\tde\bh\rA\tg\n");
	printf("a%cb%c\tc%c\tabc\n",c1,c2,c3);
	printf("\t\b%c%c", c4, c5);

	return 0;
}

结果:
在这里插入图片描述
总结:

特别注意\t和\r
区别:

  1. \t是制表符(不一定是8个或者4个),就是空几个格,这里有个公式:spaceNum = |n - 8| % 8(n代表\t前的字符个数,包括%等)
  2. \r是回车符(注意几种情况),如果回车后的字符多于前面的原始字符,则进行替换。如果不足则将原始数据进行补充
  3. \t注意光标的位置,和\t一起使用时,可能会出现局部替换的情况
  4. 字符串结束时会添加一个\0的结束符
  5. \XXX代表以8进制表示的数

参考资料:
\t的详细介绍
\r的详细介绍

(4)设a、b为int型变量,x、y为float型变量,c1、c2为char型变量,且设a=5,b=10,x=3.5,y=10.8,c1=‘A’,c2=‘B’。为了得到以下的输出格式和结果,请写出对应的printf语句。

x-y=-7.3 a-b=-5
c1=A or 65(ASCII)
c2=B or 66(ASCII)

#include <stdio.h>
int main() {
	int a = 5, b = 10;
	float x = 3.5, y = 10.8;
	char c1 = 'A', c2 = 'B';
	printf("x-y=%.1f  a-b=%d\n", x - y, a - b);
	printf("c1=%c or %d(ASCII)\n", c1, c1);
	printf("c2=%c or %d(ASCII)\n",c2,c2);
	return 0;
}

(5)若已有说明:

int a = 123;
float b = 456.78;
double c = -123.45678;

试写出以下各printf语句相应的输出结果

#include <stdio.h>
int main() {
	int a = 123;
	float b = 456.78;
	double c = -123.45678;

	printf("%.3f  %.3e  %f\n",b,b,c);
	printf("%8.3f  %8.3e   %g\n", b, b, c);
	printf("%u  %-10.3f  %-10.3e\n",a,b,c);
	return 0;
}

结果:
在这里插入图片描述

(6)对于如下语句

#include <stdio.h>
int main() {
int x=0; float y=0.0; char z=‘0’;
scanf(“%3d%f%3c”, &x, &y, &z);
printf(“x的值为:%d,y的值为:%f,z的值为:%c”, x,y,z);
return 0;
}

输入以下数据
12345abc
则x、y和z的值分别是什么?

结果:
在这里插入图片描述

2)习题 3.4 (排序算法)

写一个程序,首先向数组中输入10个得分,然后去掉一个最高分,去掉一个最低分,计算剩下的平均分,并输出。

使用冒泡排序算法

#include <stdio.h>
int main() {
	float score[10] = { 0 };
	for (int i = 0; i < 10; i++)
	{
		printf("请输入第%d位同学的得分:", i+1);
		scanf("%f", &score[i]);
	}
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10 - 1 - i; j++)
		{
			if (score[j] > score[j+1])
			{
				float temp = score[j];
				score[j] = score[j + 1];
				score[j + 1] = temp;
			}
		}
	}
	//计算剩下的平均分
	double sum = 0;
	for (int k = 1; k < 9; k++)
	{
		sum += score[k];
	}
	printf("去掉一个最高分:%.0f,去掉一个最低分:%.0f\n", score[9], score[0]);
	printf("平均分为:%.1f", sum/8);
	
	return 0;
}

使用选择排序算法

#include <stdio.h>
int main() {
	float score[10] = { 0 };
	for (int i = 0; i < 10; i++)
	{
		printf("请输入第%d位同学的得分:", i+1);
		scanf("%f", &score[i]);
	}
	for (int i = 0; i < 10; i++)
	{
		for (int j = i+1; j < 10; j++)
		{
			if (score[i] > score[j])
			{
				float temp = score[i];
				score[i] = score[j];
				score[j] = temp;
			}
		}
	}
	
	//计算剩下的平均分
	double sum = 0;
	for (int k = 1; k < 9; k++)
	{
		sum += score[k];
	}
	printf("去掉一个最高分:%.1f,去掉一个最低分:%.1f\n", score[9], score[0]);
	printf("平均分为:%.1f", sum/8);
	
	return 0;
}

总结:
冒泡排序是相邻比,选择排序是和每一个比

3)习题 3.5(指针法)

编写程序,输入一个数字序列,输出相邻两个数之间的差值。例如,输入
15 20 18 24 9 54 30
那么应该输出:
5 -2 6 -15 45 -24

解法1:

#include <stdio.h>
int main() {
	int num[7];
	int pre = 0;
	int last = 1;
	int sum = 0;
	printf("请输入一个数字序列:\n");
	for (int i = 0; i <= 6; i++)
	{
		printf("第%d个数字:", i + 1);
		scanf("%d", &num[i]);
	}
	for (; last <= 6; last++,pre++)
	{
		sum = num[last] - num[pre];
		printf("%d ", sum);
	}
	return 0;
}

总结:
可以使用指针定位

解法2:
在这里插入图片描述
在这里插入图片描述

难点说明:

思考:getchar的作用是什么,为什么上诉表达可以起到终止输入的作用?
1.scanf函数可以使用空格代表结束
测试:
在这里插入图片描述
当输入缓冲区有内容时,scanf不会继续接收输出!
 
2.getchar函数一次只能输入一个字符,当缓冲区有内容时,会读取缓冲区的内容,不会接收输入,并且getchar会读取空格、回车、换行符
3.可以使用getchar()作为输入的终止判断!
4.输入一行字符,使用getchar()!='\n’来作为终止

4)习题 3.6

编写一个程序,输入一个字符串,输出其中ASCII码最大的字符。

#include <stdio.h>
int main() {
	char s[10] = { '\0' };
	printf("请输入一个字符串(长度小于10):");
	gets_s(s, 9);
	int max = s[0];
	for (int i = 1; i < 9; i++)
	{
		max = max > s[i] ? max : s[i];
	}
	printf("ASCII码最大的字符是:%c", max);
	
	return 0;
}

总结:

  1. 字符串的输入有3种方式:gets(不安全,已经被替代)、fgets、gets_s
  2. 推荐使用fgets、gets_s,可以设定输入长度(边界),安全性更高
  3. fget(数组名,数组长度,文件指针)
  4. gets_s(数组名,长度)
  5. fegts会在数组后添加换行符
  6. 循环输入使用组合键ctrl+Z结束输入

5)习题 3.7

编写一个程序,输入一个字符串,分别统计并输出其中大写字母(A~Z)和小写字母(a ~z)出现的次数。

#include <stdio.h>
int main() {
	char s[40];
	int upperNum = 0, lowerNum = 0;
	printf("请输入一个字符串:");
	gets_s(s, 39);
	for (int i = 0; s[i] != '\0'; i++)
	{
		if (s[i] >= 'a' && s[i] <= 'z')
		{
			lowerNum++;
		}
		else if (s[i] >= 'A' && s[i] <= 'Z')
		{
			upperNum++;
		}
	}
	printf("大写字母出现的次数:%d,小写字母出现的次数:%d", upperNum, lowerNum);
	return 0;
}

总结:
1.空格不等于‘\0’,空格是空串,\0是输入语句的结束符
2.判断输入是否完成可以使用 xxx != ‘\0’

6)习题 3.8

想一想,怎么判断一个字符串是否为空串?编写程序,输入一个字符串,如果不是空串,则照原样输出;如果是空串,则输出“空串”。例如,如果输入“hello”(不含引号),则输出“你输入的是:hello”;如果什么都没有输入,则输出“你输入的是空串!”

#include <stdio.h>
int main() {
	char s[40];
	printf("请输入一个字符串:");
	gets_s(s, 39);
	if (s[0] == '\0')
	{
		printf("你输入的是空串!");
	}
	else {
		printf("你输入的是:%s", s);
	}
	return 0;
}

总结:
输出字符串可以使用%s

7)习题 3.9

运行例 3.18,产生 scores.txt 文件。然后编写程序,读取这个文件的内容并在屏幕上显示出来。
附录:3.18代码

3.18代码:

#include <stdio.h>
#include<stdlib.h>
#define N 5
int main()
{
	FILE* fp;
	char stuid[10], name[20];
	float score;
	int i;
	if ((fp = fopen("D:\\score.txt", "w")) == NULL)
	{
		printf("文件创建失败!");
		exit(0);
	}
	fprintf(fp, "学号\t姓名\t入学成绩\n");
	for ( i = 0; i < N; i++)
	{
		printf("请输入学生学号、姓名和入学成绩(空格分开):");
		scanf("%s%s%f", stuid, name, &score);
		fprintf(fp, "%s\t%s\t%f\n", stuid, name, score);
	}
	fclose(fp);
	return 0;
}

输入:
学号 姓名 入学成绩
0001 Li 580.000000
0002 Wang 571.000000
0003 Zhao 568.000000
0004 Meng 575.000000
0005 He 586.000000

代码:

#include <stdio.h>
#include<stdlib.h>
#define N 5
int main()
{
	FILE* fp;
	char no[20],name[20],score[20];
	if ((fp = fopen("D:\\score.txt", "r")) == NULL)
	{
		printf("文件创建失败!");
		exit(0);
	}
	while (fscanf(fp,"%s %s %s",no,name,score) == 3)
	{
		printf("%s %s %s\n", no, name, score);
	}
	fclose(fp);
	return 0;
}

fscanf的用法
注意返回值是读取的个数

8)习题 3.10

写一个程序,输人一个字符串,然后将其输出到一个文本文件中,格式是每个字符占一行。例如,输人“Hello”,那么输出的文本文件中应该有 6 行,如下:
H
e
l
l
o

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	FILE* fp;
	char str[20];
	if ((fp = fopen("D:\\text.txt", "w")) == NULL)
	{
		printf("文件创建失败!");
		exit(0);
	}
	gets_s(str);
	for (int i = 0; i < strlen(str); i++)
	{
		fprintf(fp,"%c\n", str[i]);
	}
	fclose(fp);
	return 0;
}

4. 运算符与表达式

1)习题 4.5

若x、y为int型变量,则执行以下语句后结果是什么?

#include <stdio.h>
int main() {
	int x, y;
	for ( x = 1; x < 5; x+=2)
	{
		for (y = 1; y < 5; y++)
			printf("%3d", x * y);
		printf("\n");
	}
	return 0;
}

结果:
在这里插入图片描述

2)习题 4.6

程序填空题。零存整取问题:每月同一天存入银行50元钱,单利计息,月利率为5%,求一年以后的本利和。

#include <stdio.h>
#define M 50
#define R 0.005
int main() {
	int i;
	float sum1 = 0, sum2 = 0;
	for ( i = 1; i <= 12;/*填空1*/ i++)
	{
		sum1 = sum1 + M;
		sum2 = sum2 + sum1 * R/*填空2*/;
	}
	printf("sum1=%.2f sum2=%.2f sum1+sum2=%.2f\n", sum1, sum2, sum1 + sum2);
	return 0;
}

结果:
在这里插入图片描述

3)习题 4.8

输入天数,将其转换为周数和天数。例如,输入17,转换为2周3天,并输出。

#include <stdio.h>
int main() {
	int day;
	printf("请输入天数:");
	scanf("%d", &day);
	printf("%d周%d天", day / 7, day % 7);
	return 0;
}

4)习题 4.9

编写程序,根据用户输入的两位数,反向显示出该数中的数字。例如,用户输入48,那么程序输出84。

说明:
对于字符串,我们定义两个指针(也可以说是索引下标),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。

解法1:使用for循环

#include <stdio.h>
int main() {
	int num[2];
	int pre;
	int last;
	printf("请输入两位数字\n");
	for (int i = 0; i < 2; i++)
	{
		printf("请输入第%d位数字:", i + 1);
		scanf("%d", &num[i]);
	}
	for (pre = 0,last = 1;pre < 2 / 2; pre++,last--)
	{
		int temp = num[pre];
		num[pre] = num[last];
		num[last] = temp;
	}
	for (int j = 0; j < 2; j++)
	{
		printf("%d", num[j]);
	}
	return 0;
}

解法2:使用while循环

#include <stdio.h>
int main() {
	int num[2];
	int pre=0;
	int last = 1;
	printf("请输入两位数字\n");
	for (int i = 0; i < 2; i++)
	{
		printf("请输入第%d位数字:", i + 1);
		scanf("%d", &num[i]);
	}
	while (pre < last) {
		int temp = num[pre];
		num[pre++] = num[last];
		num[last--] = temp;

	}
	for (int j = 0; j < 2; j++)
	{
		printf("%d", num[j]);
	}
	return 0;
}

解法3:递归

#include<stdio.h>
void rev(int n) {
	/* 这种方式对于最后一个是0的数不好处理,例如8320,结果会是238 */
	int static sum = 0;
	if (n == 0)
	{
		return sum;
	}
	else
	{
		sum = sum * 10 + n%10;
		rev(n / 10);
	}
	return sum;
}
int main() {
	//编写程序,根据用户输入的两位数,反向显示出该数中的数字。例如,用户输入48,那么程序输出84。
	int num;
	printf("请输入一个数字:");
	scanf("%d", &num);
	rev(num);
	return 0;
}

5)习题 4.10

扩展习题4.9,使得它可以处理任意位数的整数。例如,用户输入163850,那么程序输出058361;用户输入65810347,那么程序输出74301856。

1)解法1

#include <stdio.h>
int main() {
	char num[100];
	int pre=0;
	int last = 0;
	int length;
	printf("请输入任意位数的数字:");
	gets_s(num, 100);
	for (int i = 0; num[i] != '\0'; i++)
	{
		last++;
	}
	length = last;
	for (; pre < length/2 ; pre++,last--)
	{
		char temp = num[pre];
		num[pre] = num[last-1];
		num[last-1] = temp;
	}
	for (int j = 0; num[j] != '\0'; j++)
	{
		printf("%d", num[j]-'0');
	}
	return 0;
}

总结:
1.任意位,可以通过记录输入数的方式来记录,以‘\0’,作为读入的终点
2.时刻注意末位的变化
3.打印过程记得减字符0转换为数字类型,否则打印会是ascii码

解法2:可以使用递归的方式实现(真题出现过)

#include<stdio.h>
void rev(int n) {
	/* 方式1:这种方式对于最后一个是0的数不好处理,例如8320,结果会是238
	int static sum = 0;
	if (n == 0)
	{
		return sum;
	}
	else
	{
		sum = sum * 10 + n%10;
		rev(n / 10);
	}
	return sum;
	*/
	//方式2:
	printf("%d", n % 10);
	if (n >= 10)
	{
		rev(n / 10);
	}
}
int main() {
	//编写程序,根据用户输入的两位数,反向显示出该数中的数字。例如,用户输入48,那么程序输出84。
	int num;
	printf("请输入一个数字:");
	scanf("%d", &num);
	rev(num);
	return 0;
}

6)习题 4.11

计算1~10所有数的平方和

#include <stdio.h>
int main() {
	int sum = 0;
	for (int i = 1; i < 11; i++)
	{
		sum += i*i;
	}
	printf("1-10所有数的平方和为:%d", sum);
	return 0;
}

5. 分支结构

*)习题 5.1(考题!!重点关注 2018年新增)

写出下列表达式的值
(1)4>9 || 3<40 (2)!(5>10) (3)‘x’>40
(4)x’+y>200 (5)!8>5 (6)5&&8!=(5&&8)
(7)1==6>4 (8)(b=10)&&(c=0)

(1)1 、(2)1、(3)1、(4)1、(5)1、(6)1、(7)1、(8)0
尤其注意第6题,说明:先计算5再计算(8!=(5 && 8)),然后计算5&&8=1,最后计算8!=1,结果为1
记忆和理解课本90页优先级!!!
视频讲解:运算符和表达式-条件运算符、逗号表达式

*)习题 5.2(考题!!重点关注 2018年新增)

指出下列表达式的求值顺序,并计算表达式的值。对于逻辑表达式,还需指出是否存在短路现象,以及在哪一步短路。设变量 f、x、y 的初值分别为 3.6,-1,1.5
(1) x>y ll (f/10)、(2)x>0 && y>0 || !x
(3)x++|| x>y ll x+y、(4)!0 ||y && (a=x+2)

(1)值为1,先算x>y,再算(f/10),未发生短路现象
(2)值为0,先计算(x > 0 && y > 0),x>0发生短路,结果为0,再计算0 || !x = 0
(3)值为1,先计算(x++ || x > y),x++发生短路,再计算1 || x + y,1出现短路,结果为1
(4)值为1,先计算!0=1,发生短路,结果为1
补充:
视频讲解:运算符和表达式-逻辑短路性质(逻辑运算符),强烈建议听一听

*)习题 5.3(考题!!每年必有!)

请按要求写出表达式。
(1) 判断坐标为(x,y)的点是否在内径为 a、外径为b、中心在原点 O 的圆环内的表达式。
(2)判断一元二次方程 ax2+bx十c=0 有实根的表达式。
(3)写出i大于0但小于10 为“真”的表达式。
(4)写出a和b的值都大于0小于n为“假”的表达式。
(5)如图 5.6 所示,写出坐标系上阴影部分的点(x,y)所满足的 C语言表达式
在这里插入图片描述

(1) (x*x+y*y>a*a) && (x*x+y*y<b*b)
(2)pow(b,2) - 4*a*c >= 0
(3)i > 0 && i < 10
(4)!(a > 0 && a < n)&&(b>0 && b <n)
(5)x>=a && (x<=c && y>=e && y<=d || x<=b && y>=g && y<=f)
注意:
1、3、4、5此类题型的问法

1)习题 5.4

写出下列程序的运行结果

(1)

#include<stdio.h>
int main()[
	int x, a = 1,b = 1,c = 1;
	x = (a = 0) || (b = 2) || (c = 3);
	printf("x%d,a=%d,b=%d,c=%d\n",x,a,b,c);
	return 0;
}

结果:

x=1,a=0,b=2,c=1

(2)

#include <stdio.h>
int main() {
	int x, a = 0, b = 0, c = 0,d=0;
	x = (a = 1) || (b = 2) && (c = 3) && (d=4);
	printf("x=%d,a=%d,b=%d,c=%d,d=%d\n", x, a, b, c,d);
	return 0;
}

结果:
在这里插入图片描述

思考,为什么b、c、d都为0?
与短路性质和执行顺序有关,先执行(a=1),此时结果为1,由于短路或,一真则真,此时发生短路现象,后面的程序不会执行了,接着将1赋值给x,即得结果!

参考:
C/C++逻辑运算符 | | 和 && 的优先级误区——优先级决定运算顺序吗?

2)习题 5.5

(1)

(1)以下为不正确语句,为什么?
① if(x>y);
② if(x==y) x+=y;
③ if (x!=y) scanf(“%d”,&x) else scanf(“%d”,&y);
④ if(x<y){ x++;y++;)

答:
③是错误的,不能合并成一句话写,需要在第一个scanf语句结束后添加分号

(2)执行下面语句后,x、y、z、m 的值分别是多少?

#include <stdio.h>
int main() {
	int m = 3, x = 2,y = 4, z = 1;
	m = (m < x) ? m : x++;
	m = (m < y) ? m : y++;
	m = (m < z) ? m : z++;
	printf("x的值:%d,y的值:%d,z的值:%d,m的值:%d", x, y,z,m);
	return 0;
}

结果:
x的值:3,y的值:4,z的值:2,m的值:1
总结:
m的值取决于m<z后执行三元运算符的哪个表达式,而不是1/0

(3) 阅读程序,回答问题

#include<stdio.h>
int main(){
	int a,b,m=1,n=1;
	scanf("%d%d"&a,&b);
	if (a>0) m=m+n;
	if (a<b) n-2*m;
	else if (a==b) n=5;
	else n=m+n;
	printf("m=%d n=%d\n",m,n);
	return 0;
}

问题:
①:当输入为:-1 -2 程序的运行结果是什么? m=1 n=2
②:当输入为:1 0程序的运行结果是什么? m=2 n=3
③:为了输出 n=4,变量a和b应具备什么条件? a > 0 && a < b

(4) 请分析以下语句中对 w 的不同值域所进行的操作,把套的if语句改写成不嵌套的if语句。

if (w<0)k=0;
else if (w<=100)k=1;
else k=0;

答:
if(w < 0) k = 0;
if(w >= 0&&w <=100)k=1;
if(w > 100) k = 0;

3)习题 5.6

判断从键盘输入字符c的种类。将字符分成5类:
(1)控制字符(ASCII码小于32)
(2)数字字符(ASCII码在48-57之间)
(3)大写字母(ASCII码在65-90之间)
(4)小写字母(ASCII码在97-122之间)
(5)其它字符
请写出判断5种字符的表达式(要求给出的判断表达式完备、互斥,即输入任意字符c,有且仅有一个判断表达式为真);最后写出判断字符种类的顺序、嵌套或多路分支结构程序,并画出流程图。

#include <stdio.h>
int main() {
	char c;
	printf("请输入字符:");
	scanf("%c", &c);
	if (c < 32)
	{
		printf("该字符为控制字符!");
	}
	else if (c >= 48 && c <=57)
	{
		printf("该字符为数字字符!");
	}
	else if (c >= 65 && c <= 90)
	{
		printf("该字符为大写字母!");
	}
	else if (c >= 97 && c <= 122) {
		printf("该字符为小写字母!");
	}
	else
	{
		printf("该字符为其它字符!");
	}

	return 0;
}

总结:
字符可以和字符作比较,比的是ASCII码

4)习题 5.7

(1)
在这里插入图片描述

#include <stdio.h>
int main() {
	int x, y;
	printf("请输入x的值:");
	scanf("%d", &x);
	if ( x < -5 )
	{
		y = -3*x + 5;
	}
	else if (x >= -5 && x < 0)
	{
		y = x;
	}
	else if (x == 0) {
		y = 0;
	}
	else if (x > 0 && x <= 5)
	{
		y = 2 * x;
	}
	else {
		y = 4 * x - 10;
	}
	printf("y的值为:%d", y);

	return 0;
}

(2)

由键盘任意输入3个数,实现按降序输出此3个数。

#include <stdio.h>
int main() {
	int num[3];
	for (int i = 0; i < 3; i++)
	{
		printf("请输入第%d个数:",i+1);
		scanf("%d", &num[i]);
	}
	for (int j = 0; j < 3; j++)
	{
		for (int z = j+1; z < 3; z++) {
			if (num[j] < num[z])
			{
				int temp = num[j];
				num[j] = num[z];
				num[z] = temp;
			}
		}
	}

	for (int k = 0; k < 3; k++)
	{
		printf("%d\n", num[k]);
	}
	return 0;
}

(3)

输入20个学生的成绩,统计各分数段的人数。分数段为:90及90分以上,80-89分,70-79分,60-69分,60分以下。

#include <stdio.h>
int main() {
	int score;
	int x=0, y=0, z=0, i=0, j=0;
	for (int k = 0; k < 20; k++)
	{
		printf("请输入第%d个学生的成绩:",k+1);
		scanf("%d", &score);
		if (score >= 90)
		{
			x++;
		}
		else if (score >= 80 && score <= 89)
		{
			y++;
		}
		else if (score >= 70 && score <=79)
		{
			z++;
		}
		else if (score >= 60 && score <= 69)
		{
			i++;
		}
		else {
			j++;
		}
	}
	printf("90及90分以上人数有:%d\n", x);
	printf("80-89分人数有:%d\n", y);
	printf("70-79分人数有:%d\n", z);
	printf("60-69分人数有:%d\n", i);
	printf("60分以下人数有:%d\n", j);
	return 0;
}

(4)

输入一行字符,分别统计其中包含的数字、字母和其他字符的个数。

#include <stdio.h>
int main() {
	char c[30];
	printf("请输入一行字符:");
	gets_s(c, 29);
	int x = 0, y = 0, z = 0;
	for (int i = 0; c[i] != '\0'; i++)
	{
		if ((c[i] >= 'a' && c[i] <= 'z')||(c[i] >= 'A' && c[i] <= 'Z'))
		{
			x++;
		}
		else if ((c[i]-'0' >= 0) && (c[i]-'0' <= 9))
		{
			y++;
		}
		else
		{
			z++;
		}
	}
	printf("字符中包含数字:%d个\n包含字母:%d个\n包含其它字符:%d个", x, y, z);
	return 0;
}

总结:

  1. 判断是否为字母:(c[i] >= 'a' && c[i] <= 'z')||(c[i] >= 'A' && c[i] <= 'Z')
  2. 判断是否为数字:(c[i]-'0' >= 0) && (c[i]-'0' <= 9)

(8)

输入3个点的坐标,判断他们是否在同一条直线上。

解法1:

#include <stdio.h>
int main() {
	int x[3];
	int y[3];
	int k1, k2, k3;
	printf("请输入3个点的坐标\n");
	for (int i = 0; i < 3; i++)
	{
		printf("请输入第%d个点的横坐标:",i+1);
		scanf("%d", &x[i]);
		printf("请输入第%d个点的纵坐标:", i + 1);
		scanf("%d", &y[i]);
	}
	if ((x[0] == x[1] == x[2]) || (y[0] == y[1] == y[2]))
	{
		printf("3个点在同一条直线上");
	}
	else {
		k1 = (y[1] - y[0]) / (x[1] - x[0]);
		k2 = (y[2] - y[1]) / (x[2] - x[1]);
		k3 = (y[2] - y[0]) / (x[2] - x[0]);
		if (k1 == k2 && k2 == k3) {
			printf("3个点在同一条直线上");
		}
		else
		{
			printf("3个点不在同一条直线上");
		}
		
	}
	return 0;
}

总结:分三种情况考虑:
①当三条直线的斜率都存在时,若三条直线的斜率相同,输出yes;
②三条直线的斜率都不存在,即三个横坐标相同,输出yes;
③其他情况下:若三条直线的纵坐标相同,输出yes;
否则输出no;

解法2:
在这里插入图片描述

5)习题 5.8

在美国NBA中,评价球员的实力通过两个指标:平均每场得分p,平均每场篮板球数r。下面是评价标准:
(1)p>=20且r>=15,则为“最有价值球员”。
(2)p>=15且r>=10,或p>=20且r>=8,或p>=12且r>=15,则为“优秀球员”(该条件是指在评完“最有价值球员”之后满足该条件为“优秀球员”,下同)。
(3)p>=8且r>=5,则为“合格球员”。
(4)p>=8且r<5,或p<8且r>=5,则为“较差球员”。
(5)p<8且r<5,则为“很差球员”。
试画出NBA球员分类评价图,并写出判断5种球员的表达式(要求给出的判断表达式完备、互斥、即任意给定p和r的值,有且仅有一个判断表达式为真);最后写出评价球员的顺序、嵌套或多路分支结构程序,并画出流程图。


#include <stdio.h>
int main()
{
	double p, r;
 
	printf("请输入一个球员平均每场得分p:\n");
	scanf("%lf", &p);
	printf("请输入一个球员平均每场篮板球数:\n");
	scanf("%lf", &r);
 
	if (p >= 20 && r >= 15)
		printf("最有价值球员!\n");
	else if (p >= 15 && r >= 10 || p >= 20 && r >= 8 || p >= 12 && r >= 15)
		printf("优秀球员!\n");
	else if (p >= 8 && r >= 5)
		printf("合格球员!\n");
	else if (p >= 8 && r < 5 || p < 8 && r >= 5)
		printf("较差球员!");
	else
		printf("很差球员!");
 
	return 0;
}

6)习题 5.9

在我国CBA比赛中,评价球员有3项指标:平均每场得分a,平均每场篮板次数b、平均每场助攻次数c。在一个赛季中,一个球员
(1)如果3项指标都不小于10,则为“最有价值球员”。
(2)如果有两项指标不小于10,则为“全明星球员”。
(3)如果只有一项指标不小于10,则为“明星球员”。
(4)如果一项指标都没有达到10,则为“普通球员”。
请写出判断4种球员的表达式(要求给出的判断表达式完备、互斥、即任意给定a、b和c的值,有且仅有一个判断表达式为真);最后写出评价球员的顺序、嵌套或多路分支结构程序,并画出流程图。

#include <stdio.h>
int main() {
	double a, b,c;

	printf("请输入一个球员平均每场得分p:\n");
	scanf("%lf", &a);
	printf("请输入一个球员平均每场篮板球数:\n");
	scanf("%lf", &b);
	printf("请输入一个球员平均每场助攻次数:\n");
	scanf("%lf", &c);

	if (a >= 10 && b >= 10 && c >= 10)
		printf("最有价值球员!\n");
	else if ((a >= 10 && b >= 10 && c < 10) || (a < 10 && b >= 10 && c >= 10) || (a >= 10 && b < 10 && c >= 10))
		printf("全明星球员!\n");
	else if ((a >= 10 && b < 10 && c < 10) || (a < 10 && b >= 10 && c < 10) || (a < 10 && b < 10 && c >= 10))
		printf("明星球员!\n");
	else if (a <10 && b <10 && c < 10)
		printf("普通球员!");

	return 0;
}

7)习题 5.10

在某次考试中,要考英语、数学两门课程(均为百分制),分别记其成绩为a和b,平均成绩为c。评价考生水平的标准如下:
(1)优秀考生:两科成绩都不低于90分。
(2)良好考生:两科成绩都不低于80分;或有一门低于80但不低于60,另一门高于80,且平均成绩不低于80分。(是否等价于:两科都及格且平均成绩不低于80分?)
(3)中等考生:两科成绩都不低于70分;或有一门低于70但不低于60,另一门高于70,且平均成绩不低于70。(是否等价于:两科都及格且平均成绩不低于70分?)
(4)及格考生:两科成绩都不低于60分;或有一门低于60分,另一门高于60,且平均成绩不低于60分。(是否等价于:平均成绩不低于60分?)
(5)较差考生:两科都低于60分。
请写出判断5个等级考生的表达式(要求给出的判断表达式完备、互斥、即任意给定a、b和c的值,有且仅有一个判断表达式为真);最后写出评价考生的顺序、嵌套或多路分支结构程序,并画出流程图。

#include <stdio.h>
int main() {
	double a, b,c;

	printf("请输入英语成绩:\n");
	scanf("%lf", &a);
	printf("请输入数学成绩:\n");
	scanf("%lf", &b);
	c = a +b/2;

	if (a >= 90 && b >= 90 )
		printf("优秀考生!\n");
	else if ((a >= 80 && b >= 80) || (a <80 && a >= 60 && b > 80 && c >=80) || (b < 80 && b >= 60 && a > 80 && c >= 80))
		printf("良好考生!\n");
	else if ((a>=70 && b >=70) || (a < 70 && a >= 60 && b > 70 && c >= 70) || (b < 70 && b >= 60 && a > 70 && c >= 70))
		printf("中等考生!\n");
	else if ((a >= 60 && b >=60) || (a < 60 && b > 60 && c >= 60) || (b < 60 && a > 60 && c >= 60))
		printf("及格考生!");
	else if(a < 60 && b < 60)
		printf("较差考生!");
	return 0;
}

思考:
该问题的设计是否完备?即是否存在不属于这5类的考生?如果完备,请画出考生分类评价图;如果不完备,请你设计一个完备的方案,并画出考生分类评价图。

6. 循环结构与程序设计基本算法

*)习题 6.1 (程序阅读题每年必考)

(1)

阅读以下程序,写出程序运行结果。

#include <stdio.h>
int main(){
	int i=1;
	while (i<=15)
		if (++i%3!=2) continue;
		else printf("%d	  ",i);
	printf("\n");
	return 0;
}

答:
2 5 8 11 14

(2)

#include <stdio.h>
int main()(
	char str[20]="azdwgtjqmn";
	int i,count=0;
	for (i=0; str[i]!='\0'; i++){
		str[i]+=4;
		if (str[i]>'z') str[i]-=26;
		count++;
	}
	printf("str[%d]=%s\n", count, str);
	return 0;
}

答:
str[10]=edhakxnuqr

(3)

#include <stdio.h>
int main (){
	char s1[]="computing",s2[]="computer";
	int i=0;
	while (s1[i]!='\0'&& s2[i]!='\0'&& s1[i]==s2[i])
		i++;
	printf("s1-s2=%d\n",s1[i]-s2[i]);
	return 0;
}

答:
s1-s2=4

(4)

#include <stdio.h>
int main(){
	int i, j, t;
	int a[11] = { 0,8,3,20,-4,15,-20,6,50,24,8 };
	for (i = 2; i <= 10; i++)
		for (j = i; j > 1; j--)
			if (a[j - 1] > a[j]) {
				t = a[j]; a[j] = a[j - 1]; a[j - 1] = t;
			}
			else break;
	for (i = 1; i <= 10; i++)
		printf("%d ",a[i]);
	return 0;
}

答:
-20 -4 3 6 8 8 15 20 24 50

1)习题 6.2 (程序填空题每年必考)

阅读以下程序,并按题目要求在空白处填上适当内容。

(1)输出如图所示的图案(共N行,N为奇数,此时N=7)

在这里插入图片描述

#include <stdio.h>
#define N 7
#define L (80-N)/2
int main() {
	char c = 'A';
	int i, j, p;
	for ( i = 1; i <= N; i++,/*第1个空*/c++)
	{
		if (i <= (N + 1) / 2) p =/*第2个空*/ i;
		else p = /*第3个空*/ p-1;// 另外的方式:N+1-i
		for ( j = 1; j <= L+/*第4个空*/4-p; j++)
		{
			printf(" ");
		}
		for ( j = 1; j <= 2*p-1; j++)
		{
			printf("%c", c);
		}
		printf("\n");
	}
	return 0;
}

小结:
做这类题可以先从题目整体出发,根据需求,先将简单的先填写出来,不一定要按照顺序做

(2)输入20个整数,将它们按升序排序后输出。

#include <stdio.h>
#define N 20
int main() {
	int i, j, t , a[N+1];
	for (i = 1; /*第1个空*/i <= 20; i++)
		scanf("%d", &a[i]);
	for (int i = 2;/*第2个空*/ i <= 20; i++)
		for (j = i; /*第3个空*/ j > 1; j--)
			if (a[j] >= a[j - 1]) break;
			else { t = a[j]; a[j] = a[j - 1]; a[j - 1] = t; }
	i = 1;
	while (i <= N)
		printf("%d, ",/*第4个空*/ a[i++]);
	printf("\n");
	return 0;
}

总结:
1.此题是冒泡排序的变形,比较顺序是从最后一个数据往前比较,每轮确定一个最小的数
2.本题要注意避免死循环printf("%d, ", a[i++]);使用i++的目的是为了使i的值每次递增1,从而实现打印数组的元素
3.++、- -,符号在前先变化后使用,符号在后先使用后变化
4.本题还有一个细节,数据是从下标为1的位置开始存的,做题时要注意数据在数组中的位置

(3)输入两个字符串分别存放到字符数组str1和str2中,将str2中的字符串全部链接到str1中字符串的尾部,并将链接得到的更长的字符串存放在str1数组中(假设字符数组str1的长度足够大)。

#include <stdio.h>
int main() {
	char str1[81], str2[40];
	int i = 0, j = 0;
	gets_s(str1, 80);
	gets_s(str2, 39);
	while (str1[i] != '\0')
	{
		/*第1个空*/i++;
	}
	while (/*第2个空*/str2[j] != '\0')
	{
		str1[i] = str2[j];
		/*第3个空*/i++;
		j++;
	}
	str1[i] = /*第4个空*/'\0';
	puts(str1);
	return 0;
}

总结:
1.此题我将gets改为get_s,因为gets属于过时的一个字符串输入函数了,最大的问题就是安全性低,没办法限制输入的个数,容易溢出
2.最后一个空str1[i] = '\0';这么填的目的是要把之前被替换的结束符给补上。

(4)关键词:字符串长度

输入一个母字符串存放到字符数组str中,并输入一个起始位置loc和一个长度len,实现从字符数组str中的母字符串的第loc位开始截取len个字符构成一个子字符串存放到字符数组substr中,并输出字符数组substr中的子字符串。如果母字符串中从loc位开始剩余的字符个数不足len个,则只截取母字符串中从loc位开始的剩余所有字符构成子字符串。

#include <stdio.h>
#include <string.h>
int main() {
	char str[81], substr[81];
	int k = 0, n, loc, len;
	gets_s(/*第1个空*/str, 80);
	scanf("%d%d", &loc, &len);
	if (loc < 1 || loc >strlen(str) || len <= 0)
	{
		substr[0] = '\0';
	}
	else
	{
		for ( n = loc - 1;/*第2个空*/ n < len;/*第3个空*/ n++,k++)
		{
			substr[k] = str[n];
		}
		substr[k] = /*第4个空*/'\0';
	}
	printf("Sub string is %s, its length is %d\n", substr, k);
	return 0;
}

总结:
1.strlen函数: 需要添加头文件 #include <string.h>

C 库函数 size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符

2)习题 6.3

(1)

分别输出如下所示的图案:

图案1:
在这里插入图片描述
图案2:
在这里插入图片描述

图案1:

#include <stdio.h>
int main() {
	int i, j;
	for ( i = 0; i < 5; i++)
	{
		for (j = i; j < 4; j++)
		{
			printf(" ");
		}
		for ( j = 0; j < i+1; j++)
		{
			printf("%d", i + 1);
		}
		printf("\n");
	}
	return 0;
}

图案2:

#include <stdio.h>
int main() {
	char c = 'A';
	int i, j;
	for ( i = 5; i > 0; i--,c++)
	{
		for (j = 5; j > i; j--)
		{
			printf(" ");
		}
		for ( j = 0; j < i; j++)
		{
			printf("%c", c);
		}
		printf("\n");
	}
	return 0;
}

(2)递推问题

猴子吃桃子问题。猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃掉了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少个桃子。

#include <stdio.h>
int main() {
	int x = 0, j = 1;
	while (true)
	{
		x = j;
		for (int i = 0 ; i < 9; i++) {
			x = x / 2 - 1;
			if (x <= 0) //剪枝
			{
				break;
			}
		}
		if (x == 1)
		{
			printf("第1天共摘了:%d个桃子", j);
			break;
		}
		j++;
	}
	return 0;
}

注意:
1.到第10天早上,循环9次,不包括第10天,第10天没吃呢!
2.最后打印的是 j 而不是 x,x代表的是第10天时剩下的个数
3.可以适当剪枝优化,如果x小于0,则不用再往后计算了
4.结果:1534


思考:
如果每逢奇数天都吃剩下的一半,又多吃一个;每逢偶数天都吃剩下的一半,有多吃两个。到第10天早上想再吃时,只见剩下一个,再求第一天共摘了多少个桃子。

#include <stdio.h>
int main() {
	int x = 0, j = 1;
	while (true)
	{
		x = j;
		for (int i = 1; i < 10; i++) {
			if (i%2 == 0)
			{
				x = x / 2 - 2;
			}
			else
			{
				x = x / 2 - 1;
			}
			
			if (x < 0)  //剪枝
			{
				break;
			}
		}
		if (x == 1)
		{
			printf("第1天共摘了:%d个桃子", j);
			break;
		}
		j++;
	}
	return 0;
}

其它解法:由已知推未知,已知最后一天剩下一个

#include <stdio.h>
int main() {
	int num = 1, day;
	for (day = 1; day < 10; day++) {
		num = (num + 1) * 2;
	}
	printf("第一天摘了%d个桃子\n", num);

	num = 1;
	for (day = 1; day < 10; day++) {
		//奇数天吃一半多一个
		if (day % 2 != 0) {
			num = (num + 1) * 2;
		}
		else {
			num = (num + 2) * 2;
		}
	}
	printf("第一天摘了%d个桃子", num);
	return 0;
}

(3)舍罕王赏麦问题

国际象棋的发明者、古印度的宰相在国王要奖励他时,要求国王在棋盘的第一格中放一粒麦子第二格中放2粒麦子第3个格中放4粒麦子,以后每一格中放的麦粒数都是前一格的两倍,直至放满64格。如果这个奖励要兑现的话,他将得到多少立方米的麦子(1立方米的麦子约等于1.42 * 108 粒)?

#include <stdio.h>
#include <math.h>
int main() {
	double sum = 1;
	/* 写法1.
	for (int i = 0; i < 64; i++)
	{
		sum = sum * 2;
	}
	*/
	//写法2.
	for (int i = 0; i < 64; i++)
	{
		sum = sum + pow(2,i);
	}
	printf("总麦粒数:%e\n", sum);
	//printf("将得到:%E立方米的麦子\n", sum * 1 / (1.42 * pow(10, 8))); 写法1.
	printf("将得到:%E立方米的麦子", sum * 1 / 1.42e8); //写法2
	return 0;
}

总结:
1.本题涉及一个变量数据类型选取的问题,如果sum的类型是int、long等类型,由于计算的数据过于庞大,会超过取值范围,所以这里使用double类型
2.一个数的多少次方可以使用pow函数,例如a的b次方:pow(a,b),
使用pow函数要添加头文件<math.h>
3.科学计数法的表示与应用

(4)

求s = a + aa + aaa + …+ aa……a的值,其中a是一个数字。例如2
+22+222+2222+22222(此时共有5个数相加),从键盘输入a的值,以及几个数相加。

#include <stdio.h>
#include <math.h>
int main() {
	int sum = 0;
	int a;
	int num;
	printf("请输入a的值:");
	scanf("%d", &a);
	printf("请输入相加的个数:");
	scanf("%d", &num);
	for ( int i = 0;i < num; i++)
	{
		sum += a;
		a = a * 10 + 2;
	}
	printf("和为:%d", sum);
	return 0;
}

(5)

电视台点歌,收费标准为:前10分钟每分钟1.5元,10分钟后每增加1分钟付1元2小时后不再付费但6小时后又重新开始计算。计算点歌结束后的付费金额。

#include <stdio.h>
#include <math.h>
int main() {
	int money = 0, flag = 1, time;
	printf("请输入点歌的时长(分钟):");
	scanf("%d", &time);
	while (flag)
	{
		if (time > 360)
		{
			money = money + 10 * 1.5 + 110 * 1;
			time = time - 360;
		}
		else {
			if (time <= 10)
			{
				money = money + time * 1.5;
			}
			else if (time > 10 && time <= 120) 
			{
				money = money + 10 * 1.5 + (time - 10) * 1;
			}
			else if(time > 120 && time <= 360) 
			{
				money = money + 10 * 1.5 + 110 * 1;
			}
			flag = 0;
		}
	}
	printf("应付金额:%d", money);
	return 0;
}

3)习题 6.4 (重点关注4、6、7往年考过类似)

(1)穷举 ※

求abc+cba的等于给定值n(n的值从键盘输入)的所有a、b、c的组合。

#include<stdio.h>
int main() {
	int a=0, b=0, c=0, n;
	printf("请输入n的值:");
	scanf("%d", &n);
	for (int a = 0; a < 10; a++)
		for (int b = 0; b < 10; b++)
			for (int c = 0; c < 10; c++)
				if (n == ((a + c) * 101 + b * 20))
					//abc+cba=a*100+b*10+c+c*100+b*10+a=(a+c)*101+b*20;
					printf("%d %d %d\n", a, b, c);
	return 0;
}

(2)

“百钱买百鸡”是我国古代的著名数学题。题目这样描述:3文钱可以买一只公鸡,2文钱可以买一只母鸡,1文钱可以买3只小鸡。用100文钱买100只鸡,那么各有公鸡、母鸡、小鸡多少只?请用C语言编写程序求解该题。

#include<stdio.h>
int main() {
	int a = 0, b = 0, c = 0, i = 1;
	for (int a = 0; a <= 100; a++)
		for (int b = 0; b <= 100; b++)
			for (int c = 0; c <= 100; c++)
				if (3 * a + 2 * b + c/3 == 100 && a+b+c == 100 )
					printf("买法%d:公鸡有:%d只,母鸡有:%d只,小鸡有%d只\n",i++, a, b, c);
	return 0;
}

说明:
设各有x、y、z只,总价要100文钱、100只,列出表达式即可

(3)

判断不定方程5x2+7y2=23 有无整数解。

#include<stdio.h>
#include<math.h>
int main() {
	int x, y;
	for ( x = 0; x < 3; x++)
	{
		for ( y = 0; y < 2; y++)
		{
			if (5 * pow(x, 2) + 7 * pow(y, 2) == 23)
				printf("存在整数解!解为:%d", x);
		}
	}
	return 0;
}

(4)穷举综合※

将1~9共9个数分成3组,分别组成3个3位数,且使这3个3位数构成1:2:3的比例,试求出所有满足条件的 3个3 位数。例如,3个3 位数 192、384、576 满足以上条件。
视频讲解

法1.直接法

#include<stdio.h>
#include<math.h>
void repeat(int *p,int n) {
	for (int a = 0; a < 3; a++)
	{
		p[n % 10]=1;
		n /= 10;
	}
}
int allUse(int *q) {
	for (int f = 1; f < 10; f++)
	{
		if (q[f] != 1)
			return 0;
	}
	return 1;
}
int main() {
	for (int i = 123; i <= 987; i++)
	{
		for (int j = 123; j <= 987; j++)
		{
			for (int k = 123; k < 987; k++)
			{
				if (i*2 == j && i*3 == k) {
					int num[10] = { 0 };
					repeat(num, i);
					repeat(num, j);
					repeat(num, k);
					if (allUse(num) == 1)
						printf("%d %d %d\n", i, j, k);
				}
					
			}
		}
	}
	return 0;
}

法2:优化

#include<stdio.h>
#include<math.h>
void repeat(int *p,int n) {
	for (int a = 0; a < 3; a++)
	{
		p[n % 10]=1;
		n /= 10;
	}
}
int allUse(int *q) {
	for (int f = 1; f < 10; f++)
	{
		if (q[f] != 1)
			return 0;
	}
	return 1;
}
int main() {
	int i, j, k;
	for (i = 123; i <= 987; i++)
	{
		j = i * 2;
		k = i * 3;
		
		int num[10] = { 0 };
		repeat(num, i);
		repeat(num, j);
		repeat(num, k);
		if (allUse(num) == 1)
			printf("%d %d %d\n", i, j, k);	
	}
	return 0;
}

解题总结:
有时候思路可以打开,不局限于数字的个数,数字的个数有时候可以从组成数的最大最小值去考虑,本题综合了数字是否重复,穷举的条件限制等,可以反复推敲

(5)逻辑推理 ※

找赛手:两个羽毛球队进行比赛,各出3人。甲队为 A,B,C 3人,乙队为X,Y,Z 3人。已抽签决定比赛名单,有人向队员打听比赛名单,A说他不和 X 比,C 说他不和X,Z比,请编写一个程序找出 3 队赛手的名单。

#include<stdio.h>
int main() {
	char a, b, c;//"分别保存 a、b、c 的对手名单
	for (a = 'x'; a <= 'z'; a++)//穷举a可能的对手
		for (b = 'x'; b <= 'z'; b++) {//穷举b可能的对手
			if (a != b) //排除a和b与同一个人比赛
				for (c = 'x'; c <= 'z'; c++) {//穷举c可能的对手
					if (c != a && c != b) {//排除c与ab的对手相同
						if (a != 'x' && c != 'x' && c != 'z') //按题目条件排除对手
							printf("a-%c b-%c c-%c\n", a, b, c);
					}
				}
		}
	return 0;
}

(6)同构数

输出 2~1000之间的所有同构数,所谓同构数是指它出现在它的平方数的右端。
例如,5、6、25 的平方分别等于 25、36、625,所以5,6和 25 都是同构数。

#include<stdio.h>
int static sum2 = 0;
int total(int target,int count) {
	if (count > 0)
	{
		total(target / 10, count - 1);
		sum2 = sum2*10 + target % 10;
	}
	return sum2;
}
int main() {
	int i, j, k, count=0, sum;
	for (i = 2; i < 1000; i++)
	{
		k = i;     
		while (k > 0) //判断所找的数是一个几位数
		{
			count++;
			k = k / 10;
		}
		j = i * i;
		sum = 0;
		sum = total(j,count);
		if (sum == i)
		{
			printf("%d的平方是%d,%d是同构数!\n", i,j,i);
		}
		count = 0;
		sum2 = 0;
	}
	return 0;
}

本题可以积累一个小技巧,如何取出一个数中的某一部分,利用递归,真题出现了大量此技巧。

(7)阿姆斯特朗数

Armstrong 数具有如下特征:一个 n位数等于其各位数的n次方之和。例如:153=13+53+33、1634=14+64+34+44,找出2、3、4、5位的所有 Armstrong 数。
分析,本题和第4题有异曲同工,找出5位数,不要只关注5位数了,可以考虑5位数的最大值,即99999

#include<stdio.h>
#include<math.h>
int static sum = 0;
int total(int n,int count) {
	if (n > 0)
	{
		sum = sum + pow(n % 10,count);
		total(n / 10, count);
	}
	return sum;
}
int main() {
	int k, count = 0, sum2;
	for (int i = 10; i <= 99999; i++)
	{
		k = i;
		while (k > 0)
		{
			count++;
			k = k / 10;
		}
		sum2 = total(i, count);
		if (sum2 == i)
		{
			printf("%d是Armstrong数\n", i);
		}
		sum = 0;
		count = 0;
	}
	return 0;
}

7. 函数与结构化程序设计

1)习题 7.2

指出程序的输出结果。

(1)

#include <stdio.h>
void fun(int x, int y, int z) {
	z = 2 * x + 5 * y;
}
int main(){
	int m = 50;
	fun(5, 2, m);
	printf("m=%d\n", m);
	return 0;
}

答:m=50

(2)

#include <stdio.h>
int main(){
	int w = 2, k, f(int);
	for (k = 0; k < 3; k++) {
		w = f(w);
		printf("w=%d\n", w);
	}
	return 0;
}
int f(int x){
	int y;
	y = ++x;
	y += x;
	return(y);
}

w=6
w=14
w=30

(3)

#include <stdio.h>
int main(){
	float max(float, float); 
	float a = 3.8,b = 3.7,c = 2.4, maxnum;
	maxnum = max(a, max(b,c));
	printf("Max number is %.2f\n", maxnum);
	return 0;
}
float max(float x, float y){
	float z;
	z = (x > y) ? x : y;
	return(z);
}

答:Max number is 3.80

2)习题 7.5

(1)

编写一个函数 fun(float x,float a,float b,float c),求二阶多项式ax2+bx+c的值。

#include <stdio.h>
int fun(float x, float a, float b, float c) {
	int y = a * x * x + b * x + c;
	return y;
}
int main(){
	printf("多项式的值为:%d", fun(1, 2, 3, 4));
	return 0;
}

(2)

编写一个函数 prn_pict(int m,int n),输出m行n列的图形,图形的第一行由n个字符A组成,图形的第二行由 n个字符 B组成,依次类推。

#include <stdio.h>
void prn_pict(int m, int n) {
	char c = 'A';
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < n; j++)
		{
			printf("%c", c);
		}
		printf("\n");
		c++;
	}
}
int main(){
	prn_pict(3, 4);
	return 0;
}

3)习题 7.6 ※

编写两个函数,分别求两个正整数的最大公约数和最小公倍数;编写一个主函数调用这两个函数并输出结果,两个正整数在主函数中输入。

#include <stdio.h>
int max(int a,int b){//最大公约数
	int res = 1;
	while (res != 0)
	{
		res = a % b;
		a = b;
		b = res;
	}
	return a;
}
int min(int c , int d) {//最小公倍数
	int res;
	res = c * d / max(c, d);
	return res ;
}
int main(){
	int x, y;
	printf("请输入两个正整数:\n");
	scanf("%d%d",&x,&y);
	printf("最大公约数为:%d", max(x, y));
	printf("最小公倍数为:%d", min(x, y));
	return 0;
}

最小公倍数 = 两数乘积 / 最大公约数
最大公约数使用辗转相除法:利用余数

4)习题 7.8(后期补充)

本章中的四则运算表达式的计算还有很多可改进之处。改进例 7.14,允许在操作数或者操作符之前或者之后输人一个或多个空格。
例 7.14 输入一个不含括号的四则运算表达式,输出计算结果

#include <stdio.h>
#include<stdlib.h>
double getNum() {
	double num;
	scanf("%lf", &num);
	return num;
}
char getopt() {
	char c;
	c = getchar();
	return c;
}
int prepare(char op1,char op2) {
	int flag;
	if (op1 == '*' || op1 == '/')
		if (op2 == '+' || op2 == '-') flag = 1;
		else flag = 0;
	else
		if (op2 == '+' || op2 == '-') flag = 0;
		else flag = -1;

	return flag;
}
double evaluate(double value,char opt,double value2) {
	double res=0;
	switch (opt)
	{
	case '+': res = value + value2; break;
	case '-':res = value - value2; break;
	case '*':res = value * value2; break;
	case '/': if (value2 == 0) { printf("非法运算数"); exit(1); }
			else res = value / value2;
	}
	return res;
}
int main(){
	double value1, value2, value3, result;
	char op1, op2;
	printf("请输入四则运算表达式:");
	value1 = getNum();
	op1 = getopt();
	if (op1 == '\n')
	{
		result = value1;
	}
	else
	{
		value2 = getNum();
		while ((op2 = getopt()) != '\n')
		{
			value3 = getNum();
			if (prepare(op2, op1) > 0)
				value2 = evaluate(value2, op2, value3);
			else {
				value1 = evaluate(value1, op1, value2);
				op1 = op2;
				value2 = value3;
			}
		}
		result = evaluate(value1, op1, value2);
	}

	printf("结果是:%lf", result);
	return 0;
}

以上程序未处理空格

5)习题 7.9(后期补充)

编写一个程序,检查输人的简单四则运算表达式是否正确。对于不正确的表达式,需要输出首先遇到的是操作符不匹配还是操作数不匹配,例如:1 2+3 是由于操作符不匹配,1+*2 是由于操作数不匹配,1+2 * 是由于操作数不匹配等。

6)习题 7.10(后期补充)

编写一个程序,输人一个人的出生年月日和当前日期,计算出他的年龄(周岁)和距离下一次生日的天数。

7)习题 7.11(后期补充)

用本章中的伪随机数产生器模拟掷骰子,并计算出每一面出现的概率。提示:骰子有 6 面,点值分别是 1~6。用伪随机数产生器随机产生 1~ 之间的整数要统计每一面出现的概率,可以用频率值来代替,即产生一个较大数量的随机数(如 10 000个等),然后统计其中每一面出现的次数,这样就可以得到频率。

8)习题 7.14

编写函数,求一个正整数的最大素数因子。

#include <stdio.h>
#include<math.h>
int isNum(int i) {
	int m = sqrt(i);
	for (int j = 2; j <= m; j++)
	{
		if ( i % j == 0)
		{
			return 0;
		}
	}
	return 1;
}
int whoMax(int n,int n2) {
	int max2 = n > n2 ? n : n2;
	return max2;
}
int main(){
	int num, max, count = 0;
	printf("请输入一个正整数:");
	scanf("%d", &num);
	for ( int i = 1; i <= num; i++)
	{
		if ( num % i == 0)
		{
			if (isNum(i))
			{
				if (count == 0) max = i;
				else {
					max = whoMax(max, i);
					count++;
				}
			}
		}
	}
	printf("最大的素数因子是:%d", max);
	return 0;
}

9)习题 7.15

编写判断水仙花数的函数。所谓水仙花数是指一个 3 位数,其各位数字方和等于该数本身。例如,153=13+53+33

#include <stdio.h>
#include<math.h>
int static sum = 0;
int equalinit(int j) {
	if (j > 0)
	{
		sum = sum + pow(j % 10, 3);
		equalinit(j / 10);
	}
	return sum;
}
int isNum(int n) {
	int iniNum = n;
	if (iniNum == equalinit(n))
	{
		return 1;
	}
	return 0;
}

int main(){
	for (int i = 100; i < 1000; i++)
	{
		if (isNum(i))
		{
			printf("%d是水仙花数!\n", i);
		}
		sum = 0;
	}
	return 0;
}

10)习题 7.16(考题出现过)

编写判断完数的函数。所谓完数是指一个数恰好等于除它本身外的因子和。例如,6=1+2+3(6 的因子是1、2、3)。

#include <stdio.h>
#include<math.h>
int isNum(int n) {
	int sum = 1;
	for (int i = 2; i < n; i++)
		if (n % i == 0) sum = sum + i;
	if (sum == n) return 1;
	return 0;
}
int main(){
	int num;
	printf("请输入一个数:");
	scanf("%d", &num);
	if (isNum(num)) printf("数字%d是完数!", num);
	else printf("数字%d不是完数!", num);
	return 0;
}

11)习题 7.17 ※

为了计算整数的幂,通常的做法是反复执行乘法,下面是一种快速算法:

POWER INTEGER(x,n)
	pow←1
	while(n>0)
	if(n%2=1)
		pow ←pow*X
	X ←X*X
	n ←n/2
	return pow

编写一个函数,实现这种算法,并验证该算法的正确性。

#include <stdio.h>
int power_integer(int x, int n) {
	int pow = 1;
	while (n > 0) {
		if (n % 2 == 1)
			pow = pow * x;
		x = x * x;
		n = n / 2;
	}
		
	return pow;
}
int main(){
	printf("%d", power_integer(2, 6));
	return 0;
}

12)习题 7.18

整数的幂也可以递归来求,写出用递归函数求幂的程序。

#include <stdio.h>
int power_integer(int x, int n) {
	if (n == 0)
		return 1;
	else
		return x * power_integer(x,n-1);
}
int main(){
	printf("%d", power_integer(2, 6));
	return 0;
}

例题7.17和7.18可以参考网上的一篇帖子

13)习题 7.19

编写递归程序,计算两个数的最大公因数。

#include <stdio.h>
int max(int m,int n) {
	if (n == 0) return m;
	return max(n,m%n);
}
int main(){
	int a, b,max2;
	printf("请输入两个正整数:");
	scanf("%d%d", &a, &b);
	max2 = max(a, b);
	printf("最大公因数为:%d", max2);
	return 0;
}

备注:
和最大公约数一样

14)习题 7.20

编写递归函数 digt(n,j),它返回整数n从右边开始的第j位数字。例如:
digit(25364,4)=5
digit(25634,3)=6

#include <stdio.h>
int max(int m,int n) {
	if (n == 1) return m % 10;
	return max(m / 10, n - 1);
}
int main(){
	printf("%d", max(25364, 2));
	return 0;
}

8. 指针与数组

1)习题 8.1

(1)

若有以下语句,则下面()是正确的描述。
char x[] = "12345";
char y[] = { '1','2','3','4','5' };
A) x数组和y数组长度相同
B)x数组长度大于y数组长度
C) x数组长度小于y数组长度
D)x数组等价于y数组

选B,本题考察字符串和字符数组的区别,详情请查阅书本72页
字符串最后默认会添加一个'\0',并且c语言提供了很多函数用来处理字符串(例如strlen函数),字符数组无法使用

(2)

执行语句 char s[] = "Sunday";后,下面()输出语句可以输出字符串"Sunday"。
A)putchar(s);
B)puts(s);
C)scanf(“%s”,s);
D)scanf(“%s”,&s);

选B,考察函数的使用,注意putchar是输出单个字符

(3)

执行语句char s [20];后,下面()输入语句可以将字符串“I am a boy.”输入给字符数组s。
A)gets(s);
B)gets(&s);
C)scanf(“%s”,s);
D)scanf(“%s”,&s);

选A,本题考察输入函数的区别和使用,以及地址符的使用!
思考:加&和不加有什么区别?
关于数组名+&和没有&的区别
并且scanf遇到空格回车等符号会终止输入,gets可以读取空格等

(4)

执行如下语句后,输出的结果为()。
printf("%d, %d\n",EOF,NULL);
A)0,0
B)-1,0
C)0,-1
D)不确定的值(因变量没有声明)

选B,本题考察EOF和NULL的特征
1.EOF不是特殊字符,而是一个定义在头文件stdio.h的常量,一般等于-1。#define EOF (-1)。
C语言中EOF是什么意思?
2.C 库宏 NULL 是一个空指针常量的值。它可以被定义为 ((void*)0), 0 或 0L,这取决于编译器供应商。
C 库宏 - NULL

(5)

为了判断两个字符串s1和s2是否相等,应当使用()。
A)if(s1 == s2)
B)if(s1 = s2)
C)if(strcpy(s1,s2))
D)if(strcmp(s1,s2) == 0)

选D,考察字符串的比较
1.==比较的是地址
2.比较内容使用c语言提供的strcmp函数,记忆方法:str=string 、 cmp=compare,合称:字符串比较
3.C 库函数 - strcpy()用来复制字符串,记忆方法:str=string 、 cpy=copy,合称:字符串复制
4.使用string的函数需要添加头文件<string.h>

(6)

若用数组名作为函数调用时的实参,则实际上传递给形参的是()
A)数组首地址
B)数组第一个元素值
C)数组中全部元素的值
D)数组元素的个数

选A,考察函数参数的使用和定义

(7)

以下不正确的描述为()
A)调用函数时,实参可以是表达式
B)调用函数时,实参变量与形参变量可以共用内存单元
C)调用函数时,将为形参分配内存单元
D)调用函数时,实参与形参的类型必须一致

选B,考察函数参数的使用和定义

(8)

C语言规定,调用一个函数时,实参变量与形参变量之间的数据传递是()
A)地址传递
B)值传递
C)由实参传给形参,并由形参传回来给实参
D)由用户编程时指定的传递方式

选B,考察函数参数的使用和定义
这题容易错选D,就是可以理解为,虽然分为地址传递和值传递,但是地址传递的也是地址值,都是值传递,地址传递和值传递的区别在于是否双向

(9)

调用函数时,如果实参与形参均是数组名,则实参与形参之间的数据传递是()。
A)值传递,是将实参的起始地址值传给形参变量
B)值传递,是将实参数组第0个元素的值传给形参变量
C)地址传递,并且实参变量与形参变量之间是双向传递
D)可能是值传递,也可能是地址传递

选A,考察函数参数的使用和定义
传参过程不可能是双向传递,只能是单向!排除C

(10)

假设p是一个指针变量,则输出语句printf("%x\n",&p);输出的是(),输出语句printf("%x\n",*p);输出的是()。
A)指针变量p的值
B)指针变量p所指向存储单元的值
C)指针变量p的地址
D)指针变量p所指向存储单元的地址

选C、B
本题考察指针的概念及特征
对指针内容不熟悉或存在疑惑的可以参考下面这篇文章
从5个维度来看C语言指针

(11)此题b、c选项需要仔细琢磨

对于如下函数,实参与形参之间的数据传递是()。
A)值传递,将数组a的所有元素的值传给变量p
B)值传递,将数组元素a【0】的指针传给变量p
C)地址传递,将数组a的起始地址传给变量p
D)可能是值传递,也可能是地址传递

fun(int *p)
{……}
int main(){
	int a[20];
	……
	fun(a);
	……
}

选B,都是值传递,排除C、D,传递的是指针,排除A

(12)

对于如下的函数调用,被调函数fun的正确说明应该是()。
A)fun(int b[])
B)fun(int b[][20])
C)fun(int(*p)[20])
D)fun(int *p[20])

int main(){
	int a[10][20];
	……
	fun(a[0]);
	……
}

选A,考察二维数组以及传参的知识
a【0】本质还是一个一维数组,排除B
C选项是一个行指针变量
D选项形参是一个指针数组,数组里存放的是指针类型的数据

(13)

若有以下声明语句,则()是数组元素的正确引用。
int a[3][4] = {2,3,4,5,3,4,5,6,4,5,6,7};
A)a【1】+3
B)*(*(a+3)+2)
C)*(a+1)
D)(*(a+1))[3]

选D,详情看书本260页中表格介绍
A.会产生越界
B.会产生越界
C.是数组元素指针的引用

2)习题 8.2(考试题型)

程序阅读题。阅读以下程序,指出程序运行结果。

(1)指针在字符串中的运用

#include <stdio.h>
int main() {
	char* p = "Student";
	void prn_str(char *, int, int);
	prn_str(p, 6, 4);
	return 0;
}
void prn_str(char *str, int m, int n) {
	int i;
	for ( i = 1; i <= m-n; i++)
	{
		printf(" ");
	}
	for ( i = 1; i <= n; i++)
	{
		printf("%c", *str++);
	}
	printf("\n");
}

结果:注意空格
在这里插入图片描述

总结:
1.注意此时*p是指向第一个字母
2.有些小伙伴的Visual编辑器可能在你敲到char* p = "Student";这行代码就报错了!这是编译器的问题
解决方案:点这里:符合模式修改

(2)

#include <stdio.h>
int main() {
	int i, j, row = 0, col = 0, max;
	int a[3][4] = { {1,2,3,4},{9,8,7,6},{-1,-2,0,5} };
	max = a[0][0];
	for ( i = 0; i < 3; i++)
	{
		for ( j = 0; j < 4; j++)
		{
			if (a[i][j] > max)
			{
				max = a[i][j];
				row = i;
				col = j;
			}
		}
	}
	printf("max=%d,row=%d,col=%d\n", max, row, col);
	return 0;
}

结果:
在这里插入图片描述

(3)指针在二维数组中的运用

#include <stdio.h>
int main() {
	int a[3][4] = { 1,2,3,4,3,4,5,6,5,6,7,8 };
	int i, j, * p = *a;
	for ( i = 0; i < 3; i++)
	{
		for ( j = 0; j < 4; j++)
		{
			printf("%3d", *p++);
		}
		printf("\n");
	}
	return 0;
}

结果:注意空格
在这里插入图片描述

总结:
1.注意此题的* p = *a;这里是指向二维数组的第一个一维数组的地址。
2.二维数组的第一个元素的地址等于二维数组内第一个元素内的第一个元素的地址,也就是a【0】=a【0】【0】;
思考:
在这里插入图片描述
此图的结果是什么,对指针取&和*要格外小心

(4)

#include <stdio.h>
int main() {
	char a[] = "Chang";
	char* p = a;
	while (*p) {
		printf("%s\n", p++);
	}
	return 0;
}

结果:
在这里插入图片描述

1.指针和字符串结合,指向字符串第一个元素的地址
2.此题利用循环while (*p),原理是当*p读取到最后一个元素’\0’时,会终止循环
3.‘\0’ = 0,NULL = 0,EOF = -1;
深入探究

char a[] = "Chang";
char* p = a;
char* p2 = "python";//p指向的字符串只有可读权限
*p = 'a';//修改成功,此时p的指向已转向数组,不再是可读区
//*p2 = 'j';//无法修改,触发运行时异常 	
//a+=1;//编译时错误,a是常量,无法修改 	
p +=1;//修改成功,指针后移 	
puts(p);

3)习题 8.3(考试题型)

程序填空题。阅读以下程序,并按题目要求在空白处填上恰当内容。

(1)

求3*4数组内的所有元素中取最大值和最小值元素的行号和列号。

#include <stdio.h>
int main() {
	int a[3][4] = { 4,5,2,7,11,32,26,6,-4,26,5,12 };
	int i, j, minrow, mincol, maxrow, maxcol, maxval, minval;
	maxval = minval = /*第1空*/a[0][0];
	minrow = mincol = maxrow = maxcol = /*第2空*/0;
	for (i = 0; i < 3; i++) {
		for ( j = 0; j < 4; j++)
		{
			if (a[i][j] < minval) {
				/*第3空*/
				minval = a[i][j];
				minrow = i;
				mincol = j;
			}
			if (a[i][j] > maxval)
			{
				/*第4空*/
				maxval = a[i][j];
				maxrow = i;
				maxcol = j;
			}
		}
	}
	printf("maxrow=%d, maxcol=%d\n", maxrow, maxcol);
	printf("minrow=%d, mincol=%d\n", minrow, mincol);
	return 0;
}

(2)

找出3个字符串中的最小者。

#include <stdio.h>
#include<string.h>
int main() {
	char str[20], s[3][20];
	int i;
	for (i = 0; i < 3; i++)
	{
		gets_s(/*第1空*/s[i]);
	}
	if (strcmp(/*第2空*/s[0], s[1]) < 0) {
		strcpy(str, s[0]);
	}
	else
	{
		strcpy(/*第3空*/str, s[1]);
	}
	if (strcmp(/*第4空*/s[2], str) < 0)
	{
		strcpy(str, s[2]);
	}
	printf("The smallest string is: \n%s\n", str);
	return 0;
	return 0;
}

总结:
1.注意第一个空二维数组的元素获取

(3)

下面函数用于计算子串substr在母串str中第一次出现的位置,如果母串中不包含子串,则返回0值。例如,at(“ver”,“university”)返回的值为4,at(“ty”,“string”)返回的值为0。

#include <stdio.h>
int at(char *substr,char *str) {
	int i, j, post;
	for (post = 0; str[post] != /*第1空*/'\0'; post++)
	{
		i = 0; j = /*第2空*/post;
		while (substr[i]!='\0' && substr[i] == str[j])
		{
			i++;
			j++;
		}
		if (substr[i] == '\0')
		{
			return /*第3空*/post + 1;
		}
	}
	return /*第4空*/0;
}
int main() {
	//int res = at("ver", "university");
	int res = at("ty", "string");
	printf("返回值为:%d", res);
	return 0;
}

4)习题 8.4(本题涉及的知识点较多,主要考察二维数组和行指针变量的运用,建议多加体会)

指出下列函数的功能,并编写一个主函数来调用它,从而构成一个完整的C程序进行上机调试。

(1)

#include<stdio.h>
int fun1(float(*a)[5], int n) {
	float s;
	int i, j, count = 0;
	for (i = 0; i < n; i++)
	{
		for (s = 0, j = 0; j < 4; j++)
		{
			//行指针变量也可以带下标使用
			//形式上当二维数组用就可以
			s = s + a[i][j];//a[i][j] 等价于 *(*(p+i)+j)
			//*(f + 1) + 1;是一个指针
		}
		a[i][4] = s / 4;//数组的最后一个元素用来存储平均分
		if (a[i][4] >= 90)
		{
			count++;  //平均分在90分以上
		}
	}
	return count;
}
int main() {
	float f[3][5] = { {90,90,90,99},{100,0,70,89},{30,60,80,90} };
	int count = fun1(f, 3);
	printf("平均成绩在90分以上的小组个数:%d", count);
	return 0;
}

功能说明:(角度不唯一)

  1. 假设一个班级有3个小组(小组个数可自定义),每个小组有4个学生,输入每个小组成员的分数,统计小组平均成绩在90分以上的个数
  2. 假设一共有3位学生(人数可自定义),输入每位学生的4科成绩统计平均成绩在90分以上的人数

(2)

#include<stdio.h>
void fun2(float a[][5], int N) {
	/* 数组a中存放了若干个学生4门课程成绩及平均成绩 */
	float t;
	int i, j, k, p;
	for (i = 0; i < N-1; i++)
	{
		p = i;
		for (k = i+1; k < N; k++)
		{
			if (a[k][4] > a[p][4]) {//找出平均成绩最高的那位同学(类比选择排序)
				p = k;
			}
		}
		if (p != i)
		{
			//排序,将平均成绩以降序排列(从高到低)
			for ( j = 0; j <= 4; j++)
			{
				t = a[i][j];
				a[i][j] = a[p][j];
				a[p][j] = t;
			}
		}
	}
}
int main() {
	float f[3][5] = { {90,90,90,99,92.25},{100,0,70,89,64.75},{30,60,80,90,65} };
	fun2(f, 3);
	float score;
	printf("排序后的成绩如下(平均成绩从高至低):\n");
	for (int i = 0; i < 3; i++)
	{
		printf("第%d位的同学成绩:", i + 1);
		//printf("%f", *( * (f + i) + 4)); 指针法
		//printf("%f", f[i][4]); 下标法
		printf("%f", ( * (f + i))[4]);//下标指针混合法,注意[]的运算优先级高
		printf("\n");
	}
	return 0;
}

功能说明:
将若干个学生的成绩进行排序,按照平均成绩从高至低(实际运用了选择排序)

5)习题 8.5(真题出现过类似的)

编写程序,求 3X3 矩阵的对角线元素之和。

#include <stdio.h>
#define N 3
int main() {
	int a[N][N] = { 1,2,3,4,5,6,7,8,9 }, sum1 = 0, sum2 = 0;
	for (int i = 0; i < N; i++)
	{
		sum1 += a[i][i];
		sum2 += a[i][2 - i];
	}
	printf("对角线元素之和为:%d", sum1 + sum2);
	return 0;
}

6)习题 8.6(注意二分的写法)

编写一个函数,用折半查找法查找某数是否在给定的升序数组中,如果在则返回指向其出现位置的指针,否则返回NULL。在主函数中调用该函数,其中给定的数组有15个元素,它们的值在主函数中给出,待查找的数也在主函数中输人。

#include <stdio.h>
#define N 15
int* index(int *p, int target) {
	int left = 0, right = N-1,mid;
	while (left <= right)
	{
		mid = (left + right) / 2;
		if (p[mid] == target)
		{
			return &p[mid];
		}
		else if (p[mid] > target)
		{
			right = mid - 1;
		}
		else
		{
			left = mid + 1;
		}
	}
	return NULL;
}
int main() {
	int a[N];
	for (int i = 0; i < 15; i++)
	{
		a[i] = i + 1;
	}
	int *q = index(a, 8);
	printf("%d", *q);
	return 0;
}

7)习题 8.7(指针数组)

有一个班5个学生,4门课,成绩表存放在一个5行5列的二维数组中,其中每行一个学生,第0列存放学号,其他4列存放4门课程的成绩。要求编写 4个函数分别实现以下4个要求。
①找出有2门及2门以上课程不及格的所有学生,输出他们的学号。
②求第一门课的全班平均分,并将该平均分返回主函数中输出。
③找出4门课程平均成绩在 90分以上(含 90分)或全部课程成绩都在 85 分以上(含85分)的所有学生,输出他们的学号及全部课程成绩和平均成绩。
④将所有学生按成绩排序,排序后再输出所有学生各门课程的成绩表。排序的原则是:先按第一门课程成绩排序,第一门课程成绩高的排在前面,成绩低的排在后面;如果第一门课程的成绩相同,则再比较第二门课程的成绩,第二门课程成绩高的排在前面,成绩低的排在后面;如果第一门课程及第二门课程的成绩均相同,则学号在前的排在前面,学号在后的排在后面。

#include <stdio.h>
void prn_bad(int stu[][5]) {//①
	int count = 0;
	for (int i = 0; i < 5; i++) {
		for (int j = 1; j < 5; j++)
			if (stu[i][j] < 60) count++;
		if (count >= 2) printf("%d\n", stu[i][0]);
		count = 0;
	}
}
double aver(int stu[][5]) {//②
	double sum = 0;
	for (int i = 0; i < 5; i++)
		sum += stu[i][1];
	return sum / 5;
}
void prn_well(int stu[][5]) {//③
	double sum = 0;
	int count = 0;
	for (int i = 0; i < 5; i++) {
		for (int j = 1; j < 5; j++)
		{
			if (stu[i][j] >= 85) count++;
			sum += stu[i][j];
		}
		if (count == 4 || (sum / 4 >= 90)) {
			for (int k = 0; k < 5; k++)
				printf("%d\t", stu[i][k]);
			printf("%.2lf\n", (sum / 4));
		}
		count = 0;
		sum = 0;
	}
}
void prn_sort(int (*stu[5])[5]) {//④
	int (*p)[5], maxrow = 0, maxcol = 0;
	for (int i = 0; i < 5; i++)
	{
		for (int j = i+1; j < 5; j++)//比大小
		{
			if (*(*stu[i] + 1) > *(*stu[j] + 1)) maxrow = i;
			else if (*(*stu[i] + 1) == *(*stu[j] + 1)) {
				if (*(*stu[i] + 2) > *(*stu[j] + 2)) maxrow = i;
				else if (*(*stu[i] + 1) == *(*stu[j] + 1))
					if (*(*stu[i] + 0) > *(*stu[j] + 0)) maxrow = i;
					else maxrow = j;
				else
					maxrow = j;
			}
			else maxrow = j;
			//交换元素
			if (maxrow != i) {
				p = stu[i];
				stu[i] = stu[maxrow];
				stu[maxrow] = p;
			}
		}
		
	}
}
int main() {
	int stu[][5] = { {1,130,140,99,100},
					 {2,99,140,122,99},
				     {3,55,22,99,50},
				     {4,85,85,85,85},
				     {5,130,140,90,140} },(*p[5])[5];
	//prn_bad(stu);
	//printf("%.2lf", aver(stu));
	/*
	printf("学号\t成绩1\t成绩2\t成绩3\t成绩4\t平均成绩\n");
	prn_well(stu);
	*/
	for (int i = 0; i < 5; i++) 
		p[i] = stu + i;
	prn_sort(p);
	printf("学号\t成绩1\t成绩2\t成绩3\t成绩4\n");
	for (int j = 0; j < 5; j++) {
		for (int k = 0; k < 5; k++)
		{
			printf("%d\t", *(*p[j]+k));
		}
		printf("\n");
	}
		
	return 0;
}

第4问可以使用指针数组的思想去解决

第四问解法2:利用结构体

#include<stdio.h>
#define N 6
struct stu {
    int id;
    int scores[4];
}s[6];

int main() {
    FILE* fp;
    fp = fopen("D://score.txt", "r");
    stu t;
    int i, j, k;
    //读取文件数据进结构体数组中
    for (i = 0; i < N; i++) {
        fscanf(fp, "%d %d %d %d %d", &s[i].id, &s[i].scores[0], &s[i].scores[1], &s[i].scores[2], &s[i].scores[3]);
    }

    printf("排序前:\n");
    for (i = 0; i < N; i++) {
        printf("%d %d %d %d %d\n", s[i].id, s[i].scores[0], s[i].scores[1], s[i].scores[2], s[i].scores[3]);
    }


    for (i = 0; i < N; i++) {
        k = i;
        for (j = i+1; j < N; j++) {
            if (s[j].scores[0] > s[k].scores[0]) {
               k = j;//记录第一门课成绩最大的学生
            }
            else if (s[j].scores[0] == s[k].scores[0]) {//第一门课相等
                if (s[j].scores[1] > s[k].scores[1]) {
                    k = j;//更改k值记录第二门课最大的学生
                }
                else if (s[j].scores[1] == s[k].scores[1]) {//第二门课相等
                    if (s[j].id < s[i].id) {//学号小的在前面
                        k = j;//更改k值记录学号最大的学生
                    }
                }
            }
        }
        //交换两个学生
        if (k!= i) {
            t = s[i]; s[i] = s[k]; s[k] = t;
        }

    }

    printf("排序后:\n");
    for (int i = 0; i < N; i++) {
        printf("%d %d %d %d %d\n", s[i].id, s[i].scores[0], s[i].scores[1], s[i].scores[2], s[i].scores[3]);
    }
}

8)习题 8.8(要学会利用变量标记元素位置)

编写一个函数 void ad(int a[][N],int m)找出一个 m行N列(N为符号常量)的二维数组 中的“鞍点”。在主函数中输入二维数组a的值,并调用 ad 函数。所谓“鞍点"是指该位置上的元素在该行上最大,而在该列上最小(一个二维数组中可能没有鞍点)。假设二维数组 a中的所有整数都是不相等

#include <stdio.h>
#define N 3
void ad(int a[][N], int m) {
	int flag = 1, maxcol = 0;
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < N; j++)
			maxcol = a[i][0] > a[i][j] ? maxcol : j;
		for (int k = 0; k < m; k++)
			if (a[i][maxcol] > a[k][maxcol])
			{
				flag = 0;
				break;
			}
		if (flag) printf("%d\n", a[i][maxcol]);
		flag = 1;
	}
}
int main() {
	int a[][3] = { 1,3,9,2,4,6,5,7,8 };
	ad(a, 3);
	return 0;
}

9)习题 8.9 (杨辉三角)作为拓展※(后期补充)

编写一个主函数和一个函数 void yf(int n),要求是:函数yf按如下图案打印杨辉三角形的前n行;在主函数中输入n的值,并将它作为实参调用yf函数。杨辉三角形的特点是:两个腰上的数都为1,其他位置上的每一个数是它上一行相邻的两个整数之和。。要求只能利用一维数组,不能利用二维数组。
在这里插入图片描述

10)习题 8.10(字符串有关的问题统计单词)※

编写一个主函数以及两个函数 int wordCount(char * str)和int longestWords(char * str)。要求是:
①函数 wordCount 统计形参 str 所指向字符串中包含的单词个数,并返回主调函数。
②函数 longestWords 找出形参 str 所指向符串中包含的最长单词(可能有多个),并输出所有这些最长的单词,最后将最长单词的个数(大于等于 1)返回主调函数。
③在主函数中输入一个字符串,假定输入字符串中只含字母和空格,空格用来分割不同单词;以该字符串作为参数分别调用 wordCount longestWords 函数,并输出返回的结果。

#include <stdio.h>
#include <string.h> 
int wordCount(char* str)
{
	int count = 0;
	while (*str != '\0')
	{
		if (*str == ' ')
			count++;
		str++;
	}
	return count + 1;
}
int longestWords(char* str)
{
	int i, count[10], k, sum = 0, max, j = 0, l, n;//k保存每个单词的长度,j保存行,l保存列
	char b[10][100];
	for (i = 0; *(str + i) != '\0'; i++)
		if (*(str + i) == ' ')
		{
			k = i;
			while (*(str + k - 1) != ' ' && k != 0)
				k--;
			for (l = 0; k < i; k++, l++)
				*(*(b + j) + l) = *(str + k);
			*(*(b + j) + l) = '\0';
			count[j] = l;
			j++;
		}
	k = i;
	while (*(str + k - 1) != ' ' && k != 0)
		k--;
	for (l = 0; k < i; k++, l++)
		*(*(b + j) + l) = *(str + k);
	*(*(b + j) + l) = '\0';
	count[j] = l;
	j++;

	max = count[0];
	for (i = 0; i < j; i++)
	{
		if (max < count[i]) max = count[i];
	}
	for (i = 0; i < j; i++)
	{
		if (max == count[i])
		{
			sum++;
			for (n = 0; n < max; n++)
				printf("%c", b[i][n]);
			printf(" ");
		}
	}
	return sum;
}
void main()
{
	char ch1[100];
	int sum1 = 0, sum2 = 0;
	gets_s(ch1);
	sum1 = wordCount(ch1);
	printf("一共有%d个单词\n", sum1);
	printf("其中最长单词为:");
	sum2 = longestWords(ch1);
	printf("  共有%d个\n", sum2);
}

11)习题 8.11(思想与8.10类似)※

编写程序实现将键盘输入的一行字符按单词倒排输出。例如,键盘输人“Ilove you”,屏幕显示“you love I”。要求:
①编写一个函数 void invert(char *origin,char *newstr)实现按单词倒排字符串,第一个形参origin 接受实参传过来的原字符串指针,倒排后的新字符串写人字符数组newstr中。
②主函数中输入字符串,调用函数invert,输出倒排后的字符串。

#include <stdio.h>
#include <string.h>
int invertion(char* ch1, char* ch2)
{
	char b[10][20], c[20] = "";
	int i, j = 0, k, l;
	for (i = 0; *(ch1 + i); i++)
		if (*(ch1 + i) == ' ')//处理前面的j-1个单词
		{
			k = i;
			while (*(ch1 + k - 1) != ' ' && k != 0)
				k--;
			for (l = 0; k < i; k++, l++)
				*(*(b + j) + l) = ch1[k];
			*(*(b + j) + l) = '\0';
			j++;
		}
	//处理最后一个单词
	k = i;
	while (*(ch1 + k - 1) != ' ' && k != 0)
		k--;
	for (l = 0; k < i; k++, l++)
		*(*(b + j) + l) = *(ch1 + k);
	*(*(b + j) + l) = '\0';
	j++;
	strcpy(ch2, c);
	for (i = j - 1; i >= 0; i--)
	{
		strcat(ch2, *(b + i));
		strcat(ch2, " ");
		if (i == 0)
			strcat(ch2, "\0");
	}
	return j;
}
void main(void)
{
	char ch1[20], ch2[20];
	int word;
	gets_s(ch1);
	word = invertion(ch1, ch2);
	printf("\nThe sorted string is: %s", ch2);
	printf("\nThere are %d words", word);
}

12)习题 8.12(文件内容,后期补充)

回顾例8.6假定现在含有分数的文本文件的格式为:每个学生的信息占一行(行结束符为’\n’),一行中各属性之间用#隔开(每一行最后一个属性后面没有#)。其他的要求相同,请修改程序,读取并显示学生信息。

9. C程序运行原理

1)习题 9.2 (考试题型)

写出下列程序运行结果。

(1)

#include <stdio.h>
int a=4;
int fun(int x){
	static int b = 5;
	int y = 1;
	y = y + a;
	a = b + 4; 
	b = x + y; 
	return a + b + y;
}
int main(){
	int m = 5,i;
	for (i = 1; i <= 3; i++)
		printf("i=%d,%d\n", fun(m)); 
	return 0;
}

结果:
i=1,24
i=2,39
i=3,54

(2)注意障眼法

#include <stdio.h>
int main(){
	int i, j, x = 0;
	static int a[6] = { 1, 2,3,4, 5,6 };
	for (i = 0,j = 1; i < 5; ++i, j++)
	x += a[i] * a[j];
	printf("%d\n", x); 
	return 0;
}

结果:
70

2)习题 9.4(真题出现过)

编写一个函数,输入一个十进制正整数(可能大于15),将其转换为十六进制后输出。
解决方法:除16取余数倒排,取余结果放入到str1,倒排结果放入到str2

#include <stdio.h>
char* convert(int x){
	static char hexchars[] = "0123456789ABCDEF";//十六进制对应的数组
	static char str1[81], str2[81], * p = str1, * q = str2;
	static int n = 0;//计算字符长度,为倒排做准备
	//str1 存储取余数组,str2倒排存储数组
	while (x) { //取余
		*p++ = hexchars[x % 16];
		x /= 16;
		n++;
	}
	p--; //指针回退,因为多加了一步
	while (n) {
		*q++ = *p--;
		n--;
	}
	return str2;
}
int main(){
	int x;
	printf("请输入一个十进制数:\n");
	scanf("%d",&x);
	printf("对应的十六进制数是:%s\n", convert(x));
	return 0;
}

要积累转换的过程和方法

3)习题 9.5(真题出现过)

编写一个函数,输入一个十六进制的字符串,输出其对应的十进制整数(函数类型为整型)。
解题思路:声明字符串数组arr储存输入的十六进制数并将其作为子函数实参,在子函数中使用循环将arr各位转换为对应的十进制数并使用一个整型数组存储,然后各位求乘积再求和得出结果。

#include <stdio.h>
#include <string.h>
#include <math.h>
void conver(char arr[])
{
	int i, j = 0, k = 0, sum = 0, n = strlen(arr);
	int num[100];
	for (i = 0; i < n; i++)
	{
		switch (arr[i])
		{
		case 'A':num[j++] = 10;
			break;
		case 'B':num[j++] = 11;
			break;
		case 'C':num[j++] = 12;
			break;
		case 'D':num[j++] = 13;
			break;
		case 'E':num[j++] = 14;
			break;
		case 'F':num[j++] = 15;
			break;
		default:num[j++] = (int)(arr[i] - '0');
			break;
		}
	}
	for (i = n - 1; i >= 0; i--)
		sum += num[i] * pow(16, n - 1 - i);
	printf("转换结果:\n%d", sum);
}
int main()
{
	char arr[10];
	printf("请输入十六进制数:\n");
	gets_s(arr);
	conver(arr);
	return 0;
}

将16进制的数先转换为10进制表示,再利用位权相加法

11. 结构体、联合共用体与枚举类型

1)习题 11.1

(1)执行以下语句后的结果为:

#include<stdio.h>
enum weekday {
	Sun,
	Mon = 3,
	Tue,
	Wed,
	Thu
};
int main() {
	enum weekday day1, day2;
	day1 = Sun;
	day2 = Wed;
	printf("%d,%d", day1, day2);
	return 0;
}

答案:
0,5
解析:

  1. 枚举的常量默认都有序号,从0开始,可以自己手动赋值序号,在赋值序号后,该枚举后的序号从手动赋值的序号处递增
  2. 打印枚举变量实际是打印序号值,不能打印枚举的常量

(2)若有定义:

#include<stdio.h>
enum weekday {
	Mon,
	Tue,
	Wed,
	Thu,
	Fri
} workday;
int main() {
	workday = 3; //错误,不能直接将整形量赋值给枚举变量
	return 0;
}

补充:

  1. 可以间接使用枚举量为整型赋值,例如int i = Thu //此时i为3
  2. 可以利用序号来使用枚举workday = (enum weekday)(4-2)

2)习题 11.2

(1)

本题实际上是在考察共用体的内存机制,并且是用来验证计算机是大端存储还是小端存储!
详情可以观看下面的教程:
1.bilibili
2.视频笔记
3.vs变量监视教程

#include<stdio.h>
union {
	char c[2];
	int x;
}s;
int main() {
	s.x = 0x4241;
	printf("s.c[0]的十进制为:%d,s.c[1]的十进制为:%d", s.c[0], s.c[1]);
	return 0;
}

结果:
在这里插入图片描述

(2)

下面程序的功能是对两个数x1、x2的正确性进行判断,若0≤x1≤x2≤100的条件成立,则计算x12-x22,并输出计算结果;否则输出相应的错误信息,并继续输入数据,直至满足条件。

#include<stdio.h>
enum Errordata { Correct, Lt0Err, Gt100Err, LGErr };
char* Errinfo[] = { "Correct","<0 Error",">100 Error","X1>X2 Error" };
int main() {
	int n, x1, x2, error(int, int);
	do
	{
		printf("Input two numbers:(x1,x2)\n");
		scanf("%d%d", &x1, &x2);
		n = error(x1, x2);
		printf("%s\n", Errinfo[n]);
	} while (n!=Correct);
	printf("\nResult=%d\n", x1 * x1 - x2 * x2);
	return 0;
}
int error(int min, int max) {
	if (max < min) return(LGErr);
	if (max > 100) return(Gt100Err);
	if (min < 0) return(Lt0Err);
	return (Correct);
}

编程小技巧:

  1. 此题将枚举的序号与数组下标进行匹配,作匹配时可考虑使用
  2. 字符串数组的创建:char* Errinfo[]= { "XXX"…… };

3)习题 11.3

有40个学生,每个学生的数据包括学号、姓名和3门功课的成绩。请编写程序,要求从键盘输入学生的数据,并输出成绩报表(包括每个学生的学号、姓名、3门成绩及平均分数),还要求输出平均分在前5名的学生姓名和平均分。

#include<stdio.h>
struct student
{
	char no[20];
	char name[20];
	int score[3];
	double aver = 0;
};
struct student* input() {  //输入
	struct student data[6];
	struct student mes;
	for (int i = 0; i < 6; i++)
	{
		printf("请输入第%d学生的数据:\n",i+1);
		printf("请输入学生学号:");
		gets_s(mes.no);
		printf("请输入学生姓名:");
		gets_s(mes.name);
		for (int j = 0; j < 3; j++)
		{
			printf("请输入第%d门功课成绩:", j + 1);
			scanf("%d", &mes.score[j]);
			getchar();
		}
		data[i] = mes;
	}
	return data;
}
void printMes(struct student* data) {
	int i = 0;
	printf("\t\t\t成绩报表\n");
	for (i = 0; i < 6; i++)
	{
		if (i==0)
		{
			printf("学号\t|姓名\t|第一门课成绩\t|第二门课成绩\t|第三门课成绩\n");
		}
		printf("%s\t|%s\t|%d\t\t|%d\t\t|%d\n", ( * (data + i)).no, (*(data + i)).name, (*(data + i)).score[0], (*(data + i)).score[1], (*(data + i)).score[2]);
	}
}
int main() {
	struct student* data = input();
	printMes(data);
	printf("平均分在前五名的学生信息:\n");
	//先将学生信息存入一个数组里:
	struct student msg[6];
	for (int i = 0; i < 6; i++)
	{
		int sum = 0;
		for (int j = 0; j < 6; j++)
		{
			sum += (*(data + i)).score[j];
		}
		(*(data + i)).aver = sum;
		msg[i] = *(data + i);
	}
	for (int i = 0; i < 5; i++)
	{
		for (int j = 1+i; j < 6; j++)
		{
			if (msg[i].aver < msg[j].aver)
			{
				struct student temp = msg[i];
				msg[i] = msg[j];
				msg[j] = temp;
			}
		}
	}
	//输出前5名
	printf("\t\t\t成绩报表\n");
	for (int i = 0; i < 5; i++)
	{
		if (i == 0)
		{
			printf("姓名\t|平均分\n");
		}
		printf("%s\t|%.3lf\n",msg[i].name,msg[i].aver);
	}
	return 0;
}

本题要注意几个点:

  1. 输入缓存区,scanf每次输入完会有部分字符缓存在缓存区中,例如:\n,解决方案,使用getchar缓冲
    详情:1.C语言清空输入缓冲区
    2.getchar()与缓存区
  2. 本题还应注意格式化的问题(待解决)
  3. 中文乱码问题(待解决)

4)习题 11.4

编写通讯录程序,利用结构体数组,输出以下形式的通讯录:
在这里插入图片描述

#include <stdio.h>
struct member
{
	char no[30];
	char name[30];
	char phone[20];
};
int main()
{
	struct member total[4];
	for (int i = 0; i < 4; i++)
	{
		printf("请输入第%d位员工的工作证号:", i + 1);
		gets_s(total[i].no);
		printf("请输入第%d位员工的姓名:", i + 1);
		gets_s(total[i].name);
		printf("请输入第%d位员工的电话号码:", i + 1);
		gets_s(total[i].phone);
		printf("\n");
	}
	printf("工作证号\t姓名\t电话号码\n");
	for (int j = 0; j < 4; j++)
	{
		printf("%5s\t", total[j].no);
		printf("%12s\t", total[j].name);
		printf("%s\t", total[j].phone);
		printf("\n");
	}
	return 0;
}

5)习题 11.5(后期补充)

输人两个日期(年、月、日),计算这两个日期之间相隔的天数。要求写一个函数 diff,实现上面的计算。由主函数将输入的两个日期(结构体类型)传递给 diff 函数,计算后将相隔的天数返回给主函数输出。

6)习题 11.6

设有 N名考生,每个考生的数据包括考生号、姓名、性别、考试成绩,考生数据输人后,要求找出成绩最好的考生信息,请用结构指针编写程序实现查找工作。

#include <stdio.h>
#define N 3
struct stu
{
	char no[30];
	char name[30];
	char gender[10];
	double score;
};
struct stu* temp;
struct stu findMax(struct stu *p) {
	temp = p;
	struct stu MaxStu = p[0];
	for (int j = 1; j < N; j++)
	{
		if (temp->score < p[j].score)
		{
			MaxStu = p[j];
		}
	}
	return MaxStu;
}
int main()
{
	struct stu mes[N];
	for (int i = 0; i < N; i++)
	{
		printf("请输入第%d位考生的考生号:\n",i+1);
		gets_s(mes[i].no);
		printf("请输入第%d位考生的姓名:\n",i+1);
		gets_s(mes[i].name);
		printf("请输入第%d位考生的性别:\n",i+1);
		gets_s(mes[i].gender);
		printf("请输入第%d位考生的成绩:\n",i+1);
		scanf("%lf", &mes[i].score);
		getchar();
		printf("保存成功!\n");
	}
	struct stu res = findMax(mes);
	printf("考生号\t\t姓名\t性别\t考试成绩\n");
	printf("%6s\t    %8s\t%3s\t%5.2lf\n",res.no,res.name,res.gender,res.score);
	return 0;
}

7)习题 11.7(后期补充)

输人一个日期(年、月、日),并输人该年的元(即1月1日)是星期几(星期一、星期二、心星期六、星期日分别用数字 1.2…67表示),计算该日期是星期几(注意国年问题)。要求编写一个函数 week 来计算该日期是星期几,由主函数将日期(结构体类型)及元旦的星期几传递给 week 函数,并将计算得到的结果返回给主函数输出。

12. 文件

1)习题 12.3

编程把终端读入的若干行文本(按 Ctrl+C或 Ctrl+Z 组合键结束输人)复制到一个文件中。要求在复制的过程中把小写字母转换成大写字母。

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	FILE* fp;
	char ch[30];
	if ((fp = fopen("D:\\file1.txt","w"))==NULL)
	{
		printf("文件创建失败!");
		exit(0);
	}
	while((gets_s(ch)) != NULL)
	{
		for (int i = 0; i < strlen(ch); i++)
		{
			if (ch[i] >= 'a' && ch[i] <= 'z')
			{
				ch[i] -= 32;
			}
			fputc(ch[i], fp);//输出机制
		}
		fprintf(fp, "\n");
	}
	fclose(fp);
	return 0;
}

2)习题 12.4

编程将磁盘中的一个文本文件逐行逆置到另一个文件中。

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	FILE* fp, *fp2;
	char str[1024];
	char ch;
	int i = 0;
	if ((fp = fopen("D:\\file1.txt","r")) == NULL)	
	{
		printf("文件打开失败!");
		exit(0);
	}
	if ((fp2 = fopen("D:\\file2.txt","w")) == NULL)
	{
		printf("文件创建失败!");
		exit(0);
	}
	while ((ch = fgetc(fp))!=EOF)
	{
		str[i++] = ch;
		if (ch == '\n')
		{
			for (int k = i-2; k >= 0; k--)
				fputc(str[k], fp2);
			fputc('\n', fp2);
			i = 0;
		}
	}
	if (i != 0)
		for (int k = i - 2; k >= 0; k--)
			fputc(str[k], fp2);
	fclose(fp);
	fclose(fp2);
	return 0;
}

3)习题 12.5

统计文本文件中的单词个数。

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	FILE* fp;
	int i = 0;
	char ch;
	if ((fp = fopen("D:\\file1.txt","r")) == NULL)	
	{
		printf("文件打开失败!");
		exit(0);
	}
	while ((ch = fgetc(fp)) != EOF) {
		if (i == 0) i++;
		if (ch == ' ' || ch == '\n') i++;
	}
	printf("一共有%d个单词", i);
	fclose(fp);
	return 0;
}

4)习题 12.6

有一个源代码文件,其中包含有制表符(\t),编写程序对文件进行修改,将该文件中每个制表符替换为 4 个空格。

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	FILE* fp, * fp2;
	char ch;
	if ((fp = fopen("D:\\file1.txt","r")) == NULL)	
	{
		printf("文件打开失败!");
		exit(0);
	}
	if ((fp2 = fopen("D:\\file2.txt", "w")) == NULL)
	{
		printf("文件创建失败!");
		exit(0);
	}
	while ((ch = fgetc(fp)) != EOF) 
		if (ch == '\\')
			if ((ch = fgetc(fp)) == 't')
				fprintf(fp2, "    ");
			else
			{
				fputc('\\', fp2);
				fputc(ch, fp2);
			}
		else
			fputc(ch, fp2);
	fclose(fp);
	fclose(fp2);
	return 0;
}

5)习题 12.7

某班有 20名学生,期末考试科目有数学、英语、C语言3门课程。试编写一个程序,将这 20 名学生的姓名、学号及各科考试成绩存人一个二进制文件中。

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 3
struct stu {
	char name[20];
	char no[20];
	int score[3];
};
int main()
{
	FILE* fp;
	struct stu mes;
	if ((fp = fopen("D:\\file1.txt", "wb")) == NULL)
	{
		printf("文件创建失败!");
		exit(0);
	}
	for (int i = 0; i < N; i++)
	{
		printf("请输入第%d位学生的姓名:", i + 1);
		gets_s(mes.name);
		printf("请输入第%d位学生的学号:", i + 1);
		gets_s(mes.no);
		printf("请输入第%d位学生的各科成绩:", i + 1);
		scanf("%d %d %d", &mes.score[0], &mes.score[1], &mes.score[2]);
		getchar();//清除回车符
		fwrite(&mes, sizeof(mes), 1, fp);
		printf("录入成功!\n\n");
	}
	fclose(fp);
	return 0;
}

6)习题 12.8

编写程序,依据习题 12.7 得到的成绩文件中的数据,统计每名学生的总分并按总分由高到低生成考试排名结果,然后将结果写到另一文件中。

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 3
struct stu {
	char name[20];
	char no[20];
	int score[3];
};
int main()
{
	FILE* fp, * fp2;
	struct stu mes[N];
	struct stu temp;
	int sum = 0, i = 0;
	if ((fp = fopen("D:\\file1.txt", "rb")) == NULL)
	{
		printf("文件打开失败!");
		exit(0);
	}
	if ((fp2 = fopen("D:\\file2.txt", "wb")) == NULL)
	{
		printf("文件创建失败!");
		exit(0);
	}
	while (fread(&temp,sizeof(temp),1,fp) == 1)
	{
		mes[i++] = temp;
	}
	for (int i = 0; i < N; i++)
		for (int j = i+1; j < N; j++)
			if ((mes[i].score[0]+ mes[i].score[1]+ mes[i].score[2]) < (mes[j].score[0] + mes[j].score[1] + mes[j].score[2]))
			{
				temp = mes[i];
				mes[i] = mes[j];
				mes[j] = temp;
			}
	fprintf(fp2, "%-7s\t %4s\t%s\n","姓名","总分","排名");
	for (int i = 0; i < N; i++)
		fprintf(fp2,"%-7s\t%4d\t%d\n", mes[i].name, (mes[i].score[0] + mes[i].score[1] + mes[i].score[2]), i + 1);
	fclose(fp);
	fclose(fp2);
	return 0;
}

7)习题 12.9(后期补充)

设某数据文件中有 20 名学生的记录,编写程序,要求按文件中记录顺序,将偶数序的记录写人到另一文件中,并且在原文件中将它们删除。

8)习题 12.10

设有两个文件各存放有一些英文单词,请编程将它们的内容合并写人到另一个新的文件中,新文件中的单词要求按字典序存放

#include<stdio.h>
#include<string.h>

//从文件A和文件B中读取字符
void read(FILE *in,char buff[]){
	int i=0;
	char c;
	//读文件A
	if(in=fopen("D:\\A.txt","r")){ //r 打开一个已有的文本文件,允许读取文件。
		while(!feof(in)) { //feof函数 文件结束:返回非0值(EOF),文件未结束,返回0值
			c = fgetc(in);
			if(c!='\n'&&c!=' '&&c!=EOF){ //只保存一行的字母,不保存空格(EOF是文件结束标志)
				buff[i++]=c;
			}
		}
		fclose(in);   //关闭文件流
	}
	else{
		printf("无法打开文件\n");
	}
	//读文件B
	if(in=fopen("D:\\B.txt","r")){ 
		while(!feof(in))  {
			c = fgetc(in);
			if(c!='\n'&&c!=' '&&c!=EOF){ 
				buff[i++]=c;
			}
		}
		buff[i]='\0'; 
		fclose(in);   
	}
	else{
		printf("无法打开文件\n");
	}
}

//将结果写入新文件
void storage(FILE *out,char buff[]){
	int i;
	if(out=fopen("D:\\C.txt","w")){ //w 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件;如果文件存在,则该会被截断为零长度,重新写入。
		/*for(i=0;buff[i]!='\0';i++)
			fputc(buff[i],out);*/
		fwrite(buff,strlen(buff),1,out); //也可以调用fwrite函数来写文件
		printf("写入成功\n");
		fclose(out);
	}else{
		printf("无法打开文件\n");
	}
}

//快速排序
void QSort(char buff[],int low,int high){
	int i,j,tmp;
	i=low,j=high;
	if(i<j){
		tmp=buff[i];
		while(i<j){ //i与j指向同一个位置时退出
			while(i<j&&buff[j]>=tmp)
				j--;
			buff[i]=buff[j];
			while(i<j&&buff[i]<=tmp)
				i++;
			buff[j]=buff[i];
		}
		buff[i]=tmp;
		QSort(buff,low,i-1);
		QSort(buff,i+1,high);
	}
}

void main(){
	FILE *in=NULL,*out=NULL;  //将两个文件指针初始化为空,in用来读文件,out用来写文件
	char buff[255];
	int len;
	read(in,buff);       //通过文件获取信息
	len=strlen(buff);    //获取字符数组的长度
	QSort(buff,0,len-1); //对合并结果进行升序排序
	storage(out,buff);   //将排好序的结果保存到文件C中
}

  • 49
    点赞
  • 262
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
n个正整数和4个运算符,有多少种不同的合法表达式可以通过这些数字和运算符构造出来? 这个问题可以用递归的方式解决。我们可以从左到右扫描给定的数字和运算符,对于每个运算符,我们可以将其左右两边的数字分别作为左右子表达式,然后递归求解左右子表达式的值。最后将左右子表达式的值按照当前运算符进行运算,就得到了当前表达式的值。 具体实现时,我们可以使用一个递归函数,该函数的参数为当前要处理的数字和运算符的位置,以及当前已经构造出来的表达式的值。在递归过程中,我们需要判断当前位置是否已经到达了表达式的末尾,如果到达了末尾,则将当前表达式的值加入到结果集合中。否则,我们需要遍历所有可能的运算符,将左右子表达式的值递归求解,然后将它们按照当前运算符进行运算,得到当前表达式的值。 下面是一个Python实现的例子: ```python def evaluate(nums, ops, pos, res, ans): if pos == len(nums): ans.add(res) return for op in ops: if pos > 0: evaluate(nums, ops, pos + 1, eval(f"{res}{op}{nums[pos]}"), ans) else: evaluate(nums, ops, pos + 1, nums[pos], ans) nums = [1, 2, 3] ops = ['+', '-', '*', '/'] ans = set() evaluate(nums, ops, 0, 0, ans) print(len(ans)) # 输出 256 ``` 在这个例子中,我们假设给定的数字为[1, 2, 3],运算符为['+', '-', '*', '/'],然后调用evaluate函数进行计算。初始时,我们将当前位置pos和当前表达式的值res都设置为0,然后递归调用evaluate函数,将左右子表达式的值分别递归求解,并按照当前运算符进行运算,得到当前表达式的值。最后将所有不同的表达式值加入到结果集合ans中,最终输出结果集合的大小。
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值