c语言基础

C语言基础知识(案例驱动式)

第一章 基本要素

1.输出:调用printf()函数把数据输出到显示屏

#include <stdio.h>

int main(int argc, char** argv) 
{
	printf("Hello World!");
	
	return 0;
}

在这里插入图片描述

2.输出:字符串 输出

ASCII表

在这里插入图片描述

输出字符串
#include <stdio.h>
int main(int argc, char** argv) 
{
	printf("Hi,有人在吗?"); //输出给定的字符串
	printf("\n"); //输出一个换行控制符 \n 
	printf("你找谁?\n"); //输出给定字符串(尾部有一个换行符) 
	printf("\"\Mary\"\n"); //输出给定字符串(尾部有一个换行符) 
	//以下代码输出一个菱形图案(用字符和转义字符) 
	printf("  *\n"); 
	printf(" ***\n"); 
	printf("*****\n"); 
	printf(" ***\n"); 
	printf("  *\n"); 
	//用十六进制编码字符输出符号 
	printf("\x20\x20\x2A\x0A"); 
	printf("\x20\x2A\x2A*\n"); 
	//用不同方式输出 Hello!及一个换行符 
	printf("Hello!\n"); 
	printf("\x48\x65\x6C\x6C\x6F\x21\x0A"); 
	//输出特殊的英文字符 " \ % 
	printf("\"\n\\\n%%\n");  
	printf("\a\a\a\a"); //输出机器警报声 
	return 0;
}

在这里插入图片描述

输出菜单命令选项
int main(int argc, char** argv) 
{
	//输出课程信息管理系统的菜单命令选项(人机交互界面)
	printf("************************************************\n");
	printf("             欢迎使用<课程信息管理系统>\n");
printf("************************************************\n");
	printf("[A] 新增课程\n");
	printf("[B] 修改课程\n");
	printf("[C] 删除课程\n");
	printf("[D] 显示所有课程列表\n");
	printf("[E] 查询学分在某区间的课程\n");
	printf("[F] 按代号查询课程\n");
	printf("[G] 按名称查询课程\n");
	printf("[X] 退出\n");
	printf("************************************************\n");
	printf("请输入要执行的功能命令前的字母(不区分大小写):");
	
	return 0;
}

在这里插入图片描述

3.输出:占位符输出数据

在这里插入图片描述

3.1用占位符输出字符串(常量)
#include <stdio.h>

int main(int argc, char** argv) 
{
	//直接输出字符串
	printf("Hello World!\n");
	//使用字符串占位符输出指定的字符串
	printf("%s", "Hello World!\n");
	//混合输出字符串 
	printf("Hello %s!\n", "World"); 
	//指定宽度输出字符串(默认右对齐)
	printf("Hello %10s!\n", "World"); 
	//指定宽度输出字符串(指定左对齐)
	printf("Hello %-10s!\n", "World"); 
	//指定的宽度不够则对齐方式无效
	printf("Hello %3s!\n", "World"); 
	//指定的宽度不够则对齐方式无效
	printf("Hello %-3s!\n", "World"); 
	//指定的宽度不够则对齐方式无效
	printf("%3s %-3s!\n", "Hello", "World"); 
	
	//!别忘了给占位符指定输出的内容 
	printf("Hello %s\n");
	
	return 0;
}

在这里插入图片描述

3.2用占位符输出整数
#include <stdio.h>

int main(int argc, char** argv) 
{
	//整数的各种输出方式
	printf("A:%d\n", 109); //输出整数 
	printf("B:%d\n", 0x6D); //以整数形式输出十六进制数 
	printf("C:%d\n", 0x6d); //以整数形式输出十六进制数 
	printf("D:%x\n", 109); //以十六进制形式(字母小写)输出无符号整数 
	printf("E:%X\n", 109); //以十六进制形式(字母大写)输出无符号整数
	printf("F:%x\n", 0x6D); //以十六进制形式(字母小写)形式输出
	printf("G:%X\n", 0x6D); //以十六进制形式(字母大写)形式输出
	printf("H:%d\n", 'm'); //输出字母m的ASCII编码值(整数形式) 
	printf("I:%X\n", 'm'); //输出字母m的ASCII编码值(十六进制形式) 
	printf("J:%o\n", 109); //以八进制形式输出无符号整数 
	
	//忘了给占位符提供数据 
	printf("K:%d\n"); //未给占位符提供数据
	printf("L:%X\n"); //未给占位符提供数据
	
	//规定输出宽度和对齐方式(宽度足够:宽度和对齐方式有效) 
	printf("M:%-5d*%-5d*%5d*%5d*\n", 109, 109, 109,109); 
	//规定输出宽度和对齐方式(宽度不够:宽度和对齐方式无效) 
	printf("N:%-2d*%-2d*%2d*%2d*\n", 109, 109, 109,109); 
	
	return 0;
}

在这里插入图片描述

3.3用占位符输出实数
#include <stdio.h>

int main(int argc, char** argv) 
{
	//用%f输出给定的实数
	printf("A:%f %f %f %f\n", 0.0, 6.0, -0.0, -6.0);  
	printf("B:%f %f %f %f\n", 6., .6, -6., -.6);  
	printf("C:%f %f\n", 0.12345678, -0.12345678); 
	printf("D:%f %f\n", 1234.12345678, -1234.12345678); 
	
	//只指定实数占用的宽度和对齐方式,小数点后的位数默认6位 
	printf("E:*%20f*%-20f*\n", 0.12345678, -0.12345678); 
	printf("D:*%20f*%-20f*\n", 1234.12345678, -1234.12345678); 
	
	//指定占用的宽度、小数点后位数和对齐方式
	printf("F:*%20.6f*%-20.6f*\n", 0.12345678, -0.12345678); 
	printf("G:*%20.6f*%-20.6f*\n", 1234.12345678, -1234.12345678); 
	printf("H:*%20.10f*%-20.10f*\n", 0.12345678, -0.12345678); 
	printf("I:*%20.10f*%-20.10f*\n", 1234.12345678, -1234.12345678); 
	
	//规定输出宽度和对齐方式(宽度不够:宽度和对齐方式无效) 
	printf("J:*%4.6f*%-4.6f*\n", 0.12345678, -0.12345678); 
	printf("K:*%4.6f*%-4.6f*\n", 123412345678, -1234.12345678); 

	//%e或%E以科学记数法形式输出实数
	printf("L:*%e*%E*\n", 0.12345678, 12345678.0); 
	printf("M:*%20.10e*%20.10E*\n", 0.12345678, 12345678.0); 
	 
	//忘了给占位符提供数据 
	printf("N:*%f*%20.20f*\n"); //未给占位符提供数据
	
	//错误:指定输出实数却提供了整数 
	printf("O:%f  %eE\n", 123, 123); 
	
	return 0;
}

在这里插入图片描述

3.4用占位符输出字符(常量)
#include <stdio.h>

int main(int argc, char** argv) 
{
	//输出字符(可直接给定字符的编码值输出字符)
	printf("%s%c%c%c%c%c%c!%c", "Hello", ' ', 'W', '\x6F', 114, 0x6C, 'd', '\n');
	printf("%c\n", '\x6F');	//输出十六进制转义符号表示的字符 
	printf("%c\n", 114); //输出十进制编码值对应的字符 
	printf("%c\n", 0x6C);//输出十六进制编码值对应的字符 
	
	//指定输出字符占的宽度和字符对齐方式 
	printf("%s%-2c%2c%-2c%-2c%2c%2c!\n", "Hello", ' ', 'W', 'o', 'r', 'l', 'd');
	
	//!别忘了给占位符指定输出的内容 
	printf("%s%c%c%c%c%c%c%c", "Hello");
	
	return 0;
}

在这里插入图片描述

3.5用占位符输出一位学生的信息
#include <stdio.h>

int main(int argc, char** argv) 
{
	//输出一位学生的信息 
	printf("学生信息如下:\n"); //输出整数 
	printf("学号:%d\n", 101); //输出整数 
	printf("姓名:%s\n", "张三"); 
	printf("性别:%s\n", "男"); 
	printf("年龄:%d\n", 20); //输出整数 
	printf("身高:%.2f m\n", 1.75); //输出单精度实数 
	printf("体重:%.1f kg\n", 80.0); //输出单精度实数
	
	return 0;
}

在这里插入图片描述

3.6输出课程信息列表(用占位符)
int main(int argc, char** argv) 
{
	//输出课程信息列表(用占位符)  
	printf("                   课程信息\n");
	printf("------------------------------------------------\n");
	printf("%-4s  %-24s  %4s  %4s  %4s\n", "代号", "名称", "性质", "学时", "学分");
	printf("------------------------------------------------\n");
	printf("%-4d  %-24s  %4s  %4d  %4.1f\n", 1, "高等数学(上)", "必修", 72, 4.0);
	printf("%-4d  %-24s  %4s  %4d  %4.1f\n", 2, "英语听说(一)", "必修", 36, 2.0);
	printf("%-4d  %-24s  %4s  %4d  %4.1f\n", 3, "C程序设计", "必修", 54, 3.0);
	printf("------------------------------------------------\n");
	
	return 0;
}

在这里插入图片描述

4.输出结果运算

4.1算术运算

算术运算符:+、-、*(乘)、/(除)、%(求余数,模运算符)

算术表达式:5+6、5.0+6-23、2.03.0、3/2、3.0/2+3%2等

#include <stdio.h>

int main(int argc, char** argv) 
{
	//参与运算的都是整数,结果是整数 
	printf("2+3 = %d\n", 2 + 3);
	printf("2-3 = %d\n", 2 - 3);
	printf("2*3 = %d\n", 2 * 3);
	printf("2/3 = %d\n", 2 / 3); //整数相除结果是整数(舍去小数部分) 
	printf("3/2 = %f\n", 3 / 2); //输出错误 
	printf("3/2 = %d\n", 3 / 2); //整数相除结果是整数(舍去小数部分) 
	printf("3%%2 = %d\n", 3 % 2); //求余数,取模运算 
	printf("9除4的余数是 %d\n", 9 % 4); //求余数
	
	//只要有实数参与运算,结果是实数 
	printf("2+3.0 = %f\n", 2 + 3.0);
	printf("2.0-3 = %f\n", 2.0 - 3);
	printf("2.0*3 = %f\n", 2.0 * 3);
	printf("2/3.0 = %f\n", 2 / 3.0);
	printf("3.0/2.0 = %f\n", 3 / 2.0); 
	
	//字符的编码是整数,所以字符可以参与整数运算
	printf("字母A和a的ASCII编码值相差 %d\n", 'a' - 'A');
	printf("大写字母D的小写字母是 %c\n", 'D' + 32 );
	printf("小写字母e的大写字母是 %c\n", 'e' - ('a' - 'A') );
	
	return 0;
}

在这里插入图片描述

4.2关系运算

• 比较运算及关系表达式

比较运算符:>、>=、<、<=、==(等于)、!=(不等于)

关系表达式:c > (a+b)、a != b、c >= b ……

运算优先级:先算术运算后关系运算;……;里层()优先

关系表达式(比较运算)的结果(整数):1(真)或0(假)

#include <stdio.h>

int main(int argc, char** argv)
{
	printf("%d\n", 0 == 0);	 
	printf("%d\n", 0 != 0);	
	printf("%d\n", 5 == 0);	
	printf("%d\n", 0 != 5);	
	printf("%d\n", 0 > 5);	
	printf("%d\n", 5 >= 5);	

	return 0;
}

在这里插入图片描述

• 逻辑运算及逻辑表达式:复合的关系运算

逻辑运算符:&&(与)、||(或)、!(非)

逻辑表达式:a&&b、a||b、!a、a>b && a>c、ab && ac、!a && !b

运算优先级:最好用()来规定优先级

逻辑运算的对象:只有 0 和 ‘\x00’ 是假,其它都是真

逻辑表达式(逻辑运算)的结果(整数) : 1(真)或0(假)

#include <stdio.h>

int main(int argc, char** argv)
{
	printf("%d\n", !0 );
	printf("%d\n", !1 );
	printf("%d\n", !(-1) );
	printf("%d\n", !56) ;
	printf("%d\n", !(-56) );
	printf("%d\n", (0 == 0) && (0 != 0) );
	printf("%d\n", !(0 == 0) && (0 != 0) );	
	printf("%d\n", !( (0 == 0) || (0 != 0) ) );	
	printf("%d\n", !('A') );	
	printf("%d\n", !("你好!") );	

	return 0;
}

在这里插入图片描述

5.变量

怎么表示计算过程中的初始数据、中间数据和结果数据?

• 变量是用来存放数据的一 块内存空间的标识符

• 先定义,后使用

• 定义变量就是向操作系统 申请一块内存空间,并给 该空间命名

• 定义变量: 数据类型标识符 变量名

• 数据类型标识符: 整数(整型):int 实数(浮点型):float

• 给变量赋值: 变量 = 数值(常量); 变量 = 运算表达式; = 两侧的数据类型要相同

在这里插入图片描述

5.1变量定义、赋值、强制类型转换
#include <stdio.h>

int main(int argc, char** argv) 
{
	int a, b, c; //定义存放整数的三个变量 
	float f1, f2; //定义存放单精度浮点数的三个变量 
	
	//变量占用的内存单元(空间)中存储的数据是变量的值 
	printf( "1:a = %d\n", a );
	printf( "2:b = %d\n", b );
	printf( "3:c = %d\n", c );
	printf( "4:f1 = %f\n", f1 );
	printf( "5:f2 = %f\n", f2 );
	 
	//给变量赋值(常量) 
	a = 1;
	b = 2; 
	c = 2 * (a + b);
	printf( "6:a = %d\n", a );
	printf( "7:b = %d\n", b );
	printf( "8:c = %d\n", c );
	printf( "9:2 * (a + b) = %d\n", 2 * (a + b) );
	
	//强制类型转换
	f1 = (float) b;
	printf( "10:f1 = %f\n", f1 );
	printf( "11:b = %d\n", b );
	c = (int) (f1 * 3.67);
	printf( "12:f1 = %f\n", f1 );
	printf( "13:c = %d\n", c );
	printf( "14:(int)8.6 = %d\n", (int)8.6 );
	
	return 0;
}

在这里插入图片描述

5.2简单算术运算
#include <stdio.h>

int main(int argc, char** argv) 
{
	float Length; //边长
	float Perimeter; //周长 
	float Area; //面积
	
	Length = 10.0; //给定边长 
	
	//计算周长和面积 
	Perimeter = 4 * Length;
	Area = Length * Length;
	
	//输出计算结果(保留4位小数)
	printf("正方形的边长是:%.4f米\n", Length);
	printf("正方形的周长是:%.4f米\n", Perimeter);
	printf("正方形的面积是:%.4f平方米\n", Area);
	
	return 0;
}

在这里插入图片描述

5.3键盘输入数据

如果程序只能对给定的数据进行计算、输出结果,不能让用户输入数据,那么程序用途有限! C语言提供了一系列接收外部输入设备输入数据的标准函数,scanf()函数是其中一个

#include <stdio.h>

int main(int argc, char** argv) 
{
	//定义变量
	int Age; //年龄 
	float Height; //身高(实数),单位m
	float Weight; //体重(实数),单位kg

	//使用scanf()函数接收从键盘输入的数据
	printf("请输入年龄(整数),Enter键结束输入:");
	scanf("%d", &Age);
	printf("请输入身高(单位m)(实数),Enter键结束输入:");
	scanf("%f", &Height);
	printf("请输入体重(单位kg)(实数),Enter键结束输入:");
	scanf("%f", &Weight);

	//输出数据 
	printf("年龄:%d\n", Age);
	printf("身高:%.2f米\n", Height);
	printf("体重:%.1f千克\n", Weight);
	printf("BMI:%.1f千克/平方米\n", Weight / (Height * Height) );
	
	return 0;
}

在这里插入图片描述

5.4常用标准数学函数
#include <stdio.h> //标准输入输出函数 
#include <math.h> //标准数学函数 

int main(int argc, char** argv) 
{
	double x, y, z; //3个双精度实数变量
	
	x = 20.0; 
	y = sin(x);
	z = cos(x) + 0.1;
	
	printf("sin(%.1Lf) = %Lf\n", x, y);
	printf("sin(20.0) = %Lf\n", sin(20.0));
	printf("cos(%.1Lf) + 0.1 = %Lf\n", x, z);
	printf("cos(20.0) + 0.1 = %Lf\n", cos(20.0) + 0.1);
	
	return 0;
}

在这里插入图片描述

6.自定义函数



#include <stdio.h>
#include <math.h>

//功能:计算两点之间的直线距离 
double DistanceOfTwoPoint(double x1, double y1, double x2, double y2);

//功能:计算圆的面积 
double AreaOfCircle(double r);

int main() 
{

	//调用自定义函数计算两点之间的直线距离,反斜杠表示代码太长写在下一个行
	printf("(0,0)到(10,10)之间的直线距离 = %0.2Lf\n", \
		DistanceOfTwoPoint(0.0, 0.0, 10.0, 10.0) );

	//调用自定义函数计算圆的面积 
	printf("半径为10的圆的面积 = %0.2Lf\n", AreaOfCircle(10.0)) ;
	
	return 0;
}

double DistanceOfTwoPoint(double x1, double y1, double x2, double y2) 
{
	double Distance;
	
	Distance = sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
	
	return Distance; //返回计算结果 
}

double AreaOfCircle(double r)
{
	return 3.14 * r * r; //返回计算结果
}

在这里插入图片描述

另外,代码风格和debug能力要提高!

第二章 分支结构

1.问题提出

在这里插入图片描述

2.分支结构

if (条件) 语句/语句块

else 语句/语句块

• 若if后括号内的条件成立(真), 执行if之后的语句或语句块,否则执行else之后的语句或语句块

• 条件:可以是任何常量、变量、 算术表达式、关系表达式、逻辑 表达式。值为0则假,非0即真

• 语句可以是空的,语句块也如此

• 如果表达式为假什么也不做,则不用写else分支

3.问题解决

3.1问题一:简单分支

问题一分析:用户输入任意一个数值x,求函数值y。计算过程的语言描述:若x<0,y = x;否则,y = x * x

#include <stdio.h>

int main(int argc, char** argv)
{
	double x, y; //y存放分段函数计算结果
	
	printf("请输入x(数值),回车结束输入:");
	scanf("%Lf", &x);
	
	if(x < 0)  
	{ 
		y = x;
		printf("f(x) = x = %Lf\n", y);
	}
	else
	{ 
		y = x * x;
		printf("f(x) = x*x = %Lf\n", y);
	}

	return 0;
}
3.2问题二:随机数分支

如何将0-RAND_MAX之间的整数映射到 1-100之间?

• 将[0,RAND_MAX]中的实数x映射到[0,99]中的公式: 99.0/RAND_MAX * x,99.0/RAND_MAX为缩放比例

• 映射得到的数加1就在[1, 100]中

• 由于映射时x是一个实数,在C程序中需要将映射得 到的数转换为整数:(int)(实数) 将实数取整


int main(int argc, char** argv)
{
	double Ratio = 99.0 / RAND_MAX; //缩放比例 
	int Number;

	srand( time(NULL) ); //设置随机数种子 
	Number = 1 + (int)( Ratio * rand() );
	
	//输出随机数大小 
	if(Number < 50) 
	{
		printf( "小(%d)\n", Number );
	}
	else
	{
		printf( "大(%d)\n", Number );
	}

	return 0;
}
3.3问题三:嵌套循环

在这里插入图片描述


#include <stdio.h>

int main(int argc, char** argv)
{
	double Score; //成绩 
	
	//输入成绩
	printf( "请输入成绩(0-100之间):" );
	scanf( "%Lf", &Score ) ;
	
	//输出成绩及其等级 
	printf( "成绩 %5.1Lf  ", Score );
	if( Score >= 90 && Score <= 100 ) 
	{
		printf( "优\n" );
	}
	else   
	{ //x∈[0,90)
		if( Score >= 80 && Score < 90 ) 
		{
			printf( "良\n" );
		}
		else
		{ //x∈[0,80)
			if( Score >= 70 && Score < 80) 
			{
				printf( "中\n" );
			}
			else
			{ //x∈[0,70)
				if( Score >= 60 && Score < 70 ) 
				{
					printf( "及格\n" );
				}
				else 
				{ //x∈[0,60)
					printf( "不及格\n" );
				}
			}
		} 
	}

	return 0;
}

以上代码逻辑冗余,健壮性不足 改进方法:

对于判断条件完整互斥的多分支,用if…else if… else…多分支结构:代码更简洁、更易读

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv){
	double Score; //成绩 
	
	//输入成绩
	Score = 101; //输入非数值时Score值也是错的
	printf( "请输入成绩(0-100之间):" );
	scanf( "%Lf", &Score ) ;
	//检查输入的数据,保证程序的健壮性
	if( Score < 0 || Score > 100 )
	{
		printf("您输入的成绩错误!\n");
		exit(0); //终止程序,不再执行后面的代码 
	}
	
	//输出成绩及其等级 
	printf( "成绩 %5.1Lf  ", Score );
	if( Score >= 90 ) printf( "优\n" );
	else if( Score >= 80 ) printf( "良\n" );
	else if( Score >= 70 ) printf( "中\n" );
	else if( Score >= 60 ) printf( "及格\n" );
	else printf( "不及格\n" );

	return 0;
}
3.4问题四:综合复杂分支

功能需求分析:

① 输出命令行交互界面的菜单命令选项

② 接收用户输入的菜单命令字母(大小写均可)

③ 根据接收到的命令输出菜单命令字符串 数据(变量)需求分析:一个字符变量存放接收到的命令字符

准备工作:

  1. 用函数封装“输出命令行交互界面的菜单命令选项”功能: 定义一个函数实现功能①(让程序结构更清晰)

  2. 定义字符变量:char CommandLetter;

  3. 学习使用另一个从标准输入获取字符的标准函数getchar()

    在这里插入图片描述

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

/*****************************************
功能:输出菜单界面 
*****************************************/
void F_PrintMenu( void ); 

/*****************************************
功能:新增课程 
*****************************************/
void F_NewCourse( void ); 

/*****************************************
功能:修改课程 
*****************************************/
void F_UpdateCourse( void ); 

/*****************************************
功能:删除课程  
*****************************************/
void F_DeleteCourse( void ); 

/*****************************************
功能:显示所有课程 
*****************************************/
void F_ShowAllCourse( void ); 

/*****************************************
功能:查询学分在某区间的课程
*****************************************/
void F_QueryCourseByCredit( void ); 

/*****************************************
功能:按代号查询课程
*****************************************/
void F_QueryCourseByID( void ); 

/*****************************************
功能:按名称查询课程
*****************************************/
void F_QueryCourseByName( void ); 

void F_QueryCourseByName( void )
{
	printf("GGGG 按名称查询课程\n"); 
}

void F_QueryCourseByID( void )
{
	printf("FFFF 按代号查询课程\n");
}

void F_QueryCourseByCredit( void )
{
	printf("EEEE 查询学分在某区间的课程\n");
}

void F_ShowAllCourse( void )
{
	printf("DDDD 显示所有课程列表\n");
}

void F_DeleteCourse( void )
{
	printf("CCCC 删除课程\n");
}

void F_UpdateCourse( void )
{
	printf("BBBB 修改课程\n");	
}

void F_NewCourse( void )
{
	printf("AAAA 新增课程\n");	
}

void F_PrintMenu( void )
{
	//输出课程信息管理系统的菜单命令选项(人机交互界面)
	printf("************************************************\n");
	printf("             欢迎使用<课程信息管理系统>\n");
	printf("************************************************\n");
	printf("[A] 新增课程\n");
	printf("[B] 修改课程\n");
	printf("[C] 删除课程\n");
	printf("[D] 显示所有课程列表\n");
	printf("[E] 查询学分在某区间的课程\n");
	printf("[F] 按代号查询课程\n");
	printf("[G] 按名称查询课程\n");
	printf("[X] 退出\n");
	printf("************************************************\n");
	printf("请输入要执行的功能命令前的字母(不区分大小写):");
}

int main(int argc, char** argv) 
{
	//定义变量
	char CommandLetter; //命令 字母 
	 
	//输出课程信息管理系统的菜单命令选项(人机交互界面)
	F_PrintMenu();
	
	//用户输入命令字母
	scanf("%c", &CommandLetter); 
	CommandLetter = tolower(CommandLetter); //转换成小写字母 

	//根据接收到的命令执行相应的操作
	//字母a-g、x是正确输入,其余的都不正确
	if(CommandLetter == 'a')		F_NewCourse();
	else if(CommandLetter == 'b')	F_UpdateCourse();
	else if(CommandLetter == 'c')	F_DeleteCourse();
	else if(CommandLetter == 'd')	F_ShowAllCourse();
	else if(CommandLetter == 'e')	F_QueryCourseByCredit();
	else if(CommandLetter == 'f')	F_QueryCourseByID();
	else if(CommandLetter == 'g')	F_QueryCourseByName();
	else if(CommandLetter == 'x')
	{
		printf("XXXX 退出\n");
		exit(0);
	}
	else printf("输入的命令字母不存在");

	return 0;
}

在这里插入图片描述

第三章 循环结构

1.问题提出

在这里插入图片描述

2.循环结构

2.1当型循环结构

当型循环结构(语句):

while( 条件 ){

​ 循环体

}

• 循环体是一系列语句,语句可以是空语句、 单行、顺序结构、分支结构和循环结构

• 当型循环结构可以搞定一切循环!

当型循环结构 while(条件){循环体} 的执行过程:

① 计算条件,判断真假:

​ • 若条件为真,进入循环体②

​ • 否则,不进入循环体,转到循环结构之后

② 执行循环体中的语句:

​ • 若遇到 break; 语句,跳出循环结构,转到循环结构之后

​ • 若遇到 continue; 语句,转到①

​ • 否则,执行完循环体,转到①


#include <stdio.h>

int main(int argc, char *argv[]){
	while ( 1 ) {
		printf( "Hello world!\n" );
		break; //执行该语句,跳出循环 
		printf( "永远不会输出这一行!\n" );
	}

	return 0;
}

在这里插入图片描述

问题一

① 先实现完成一次猜大小的程序:生成 随机数;提示用户猜大小,输入1表 示大,0表示小;输出用户是否猜对

② 构造一个死循环:把用户猜一次大小 的代码放在循环体内(需要把变量定 义和随机数初始化代码放在循环之 外)。这样可让用户反复猜大小

③ 在循环体中最后部分加上:提示用户 是否继续玩,如果不继续输入0,否 则输入其它任意键,接收用户的输入, 然后判断用户是否不继续玩,如果不 继续,使用 break; 语句,跳出循环

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[]) {
	int RandNumber; //随机数 
	int UserNumber; //用户输入的数 1或0 

	printf( "***********************************\n" );
	printf( "    欢迎猜大小!纯粹娱乐!不赌博!\n" );
	printf( "***********************************\n" );
		
	srand( time(NULL) ); //设置随机数种子 
	
	UserNumber = 1; //初始条件,使循环条件为真 
	while( UserNumber ) {
		RandNumber = rand(); //生成随机数	
	
		//用户猜正反面
		UserNumber = -1;
		printf( "请猜正反面,正面输入 1,反面输入 0:" ); 
		scanf( "%d", &UserNumber );
		if( UserNumber != 1 && UserNumber != 0 ) {
			printf( "输入错误!\n" ); 
		}
		else {  //输出猜对否 
			if( UserNumber == (RandNumber > RAND_MAX/2) ) {
				printf( "^:^恭喜您猜对了!^:^\n" );	
			}
			else printf( "^:^很遗憾!您猜错了!^:^\n" );	
		}
		
		//提示用户是否继续猜
		UserNumber = -1;
		printf( "是否继续玩?停止输入0,否则输入1:" ); 
		scanf( "%d", &UserNumber );
	}
	
	printf( "***********************************\n" );
	printf( "^:^ 谢谢光临,下次再来玩 ^:^\n" );

	return 0;
}

在这里插入图片描述

问题二

算法设计:

• 变量:sum(累计和),k(1…n中的整数)

• 求和开始前sum和k的初值分别是0和1

• 求和:当k≤n,循环做以下计算“把k值 加到sum中,然后k的值增加1”

• 输出累计和(sum的值)

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
	int n, k, sum; 
	
	//输入上限n 
	n = 0; //防止乱输入 
	printf( "请输入n(正整数):" );
	scanf( "%d", &n );
	if ( n < 1 ) {
		printf( "输入错误!\n" );
		exit( 0 );
	}
	
	//循环求1...n的和
	k = 1; //k从1开始 
	sum = 0; //开始时和为0 
	while( k <= n ) {
		sum += k; //把k的值加到sum中 
		k++; //k自加1 
	}
	
	//输出计算结果
	printf( "1...%d 的累积和是 %d\n", n, sum );

	return 0;
}

在这里插入图片描述

2.2For型循环

for型循环:

for ( 语句1; 条件; 语句2 ) {

​ 循环体

}

• 语句1:通常是变量初始化赋值语句,可以有多个,赋 值表达式之间用逗号分隔开;可以是空语句

• 语句2:通常是改变循环变量值的语句 执行过程:

① 执行语句1

② 计算条件,判断真假:若真,进入循环体,转到③; 否,循环结束,跳到循环结构后

③ 执行循环体

​ • 若遇到 break; 语句,循环结束,跳到循环结构后

​ • 若遇到 continue; 语句,转到④

​ • 否则,执行完循环体,转到④

④ 执行语句2,转到②

#include <stdio.h>
#include <stdlib.h>
//	输出从1到每个数的累计和
int main(int argc, char *argv[]){
	int n, k, sum; 
	
	//输入上限n 
	n = 0; //防止乱输入 
	printf( "请输入n(正整数):" );
	scanf( "%d", &n );
	if ( n < 1 ) {
		printf( "输入错误!\n" );
		exit( 0 );
	}
	
	//求1...n的和
	for ( k = 1, sum = 0; k <= n; k++ ){
		sum += k; //把k的值加到sum中 
		//输出1...k的累计和
		printf( "1...%-3d的累积和 %d\n", k, sum );
	} 

	return 0;
}

在这里插入图片描述

2.3直到型循环结构

直到型循环结构 :do{循环体} while(条件);

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
	int n, k, sum; 
	
	//输入上限n 
	n = 0; //防止乱输入 
	printf( "请输入n(正整数):" );
	scanf( "%d", &n );
	if ( n < 1 ) {
		printf( "输入错误!\n" );
		exit( 0 );
	}
	
	//求1...n的和
	k = 1; //k从1开始 
	sum = 0; //开始时和为0 
	do {
		sum += k; //把k的值加到sum中 
		//输出1...k的累计和
		printf( "1...%-3d的累积和 %d\n", k, sum );
		k++; //注意输出了之后再自加 
	} while( k <= n );

	return 0;
}

在这里插入图片描述

问题三

★实现 输出 1-n 中的素数 的程序

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
	int n; //上边界 
	int k; //自然数k 
	int IsPrime; //k是否是素数的标志
	int i; //除数,从 2 到 k/2 
	
	//输入上限n 
	n = 0; //防止乱输入 
	printf( "请输入n(正整数):" );
	scanf( "%d", &n );
	if ( n < 1 ) {
		printf( "输入错误!\n" );
		exit( 0 );
	}
	
	//k = 1->n,逐一判断k是否是素数
	for ( k = 1; k <= n; k++ ) {
		//判断 k 是否是素数  
		IsPrime = 1; //假定k是素数
		for ( i = 2; i <= (k/2); i++ ) {
			if ( k % i == 0 ) {
				IsPrime = 0; //k不是素数 
				break; //终止循环 
			} 
		}
	
		//输出素数
		if ( IsPrime ) printf( "%d 是素数\n", k );
	}
	
	//给任务已经完成的提示 
	printf( "已检查完%d以内的全部自然数!\n", n );

	return 0;
}

在这里插入图片描述

问题四

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int i, j; //行号和列号
	int RowNumber; //输出*的总行数
	int StarNumber; //每一行上输出*个数 
	
	//输入总的行数 RowNumber 
	RowNumber = 0; //防止乱输入 
	printf( "请输入总行数(3-20之间):" );
	scanf( "%d", &RowNumber );
	if ( RowNumber < 3 || RowNumber > 20 ) {
		printf( "输入错误!\n" );
		exit( 0 );
	}
	
	//循环输出 RowNumber 行的* 
	for ( i = 0; i < RowNumber; i++ ){
		//第i行输出 ColumnNumber 个* 
		StarNumber = 2 * i + 1;
		for( j = 0; j < StarNumber; j++ ){
			printf( "*" );
		}
		
		printf( "\n" ); //第i行尾部换行
	}

	return 0;
}

在这里插入图片描述

问题五(综合案例)

对第二章的问题四再深入理解编码(需要理解键盘缓冲区)

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

/*****************************************
功能:输出菜单界面 
参数:无
返回:无 
*****************************************/
void F_PrintMenu( void ); 

/*****************************************
功能:新增课程 
参数:无
返回:无 
*****************************************/
void F_NewCourse( void ); 

/*****************************************
功能:修改课程 
参数:无
返回:无 
*****************************************/
void F_UpdateCourse( void ); 

/*****************************************
功能:删除课程 
参数:无
返回:无 
*****************************************/
void F_DeleteCourse( void ); 

/*****************************************
功能:显示所有课程 
参数:无
返回:无 
*****************************************/
void F_ShowAllCourse( void ); 

/*****************************************
功能:查询学分在某区间的课程
参数:无
返回:无 
*****************************************/
void F_QueryCourseByCredit( void ); 

/*****************************************
功能:按代号查询课程
参数:无
返回:无 
*****************************************/
void F_QueryCourseByID( void ); 

/*****************************************
功能:按名称查询课程
参数:无
返回:无 
*****************************************/
void F_QueryCourseByName( void ); 

void F_QueryCourseByName( void )
{
	printf("GGGG 按名称查询课程\n"); 
}

void F_QueryCourseByID( void )
{
	printf("FFFF 按代号查询课程\n");
}

void F_QueryCourseByCredit( void )
{
	printf("EEEE 查询学分在某区间的课程\n");
}

void F_ShowAllCourse( void )
{
	printf("DDDD 显示所有课程列表\n");
}

void F_DeleteCourse( void )
{
	printf("CCCC 删除课程\n");
}

void F_UpdateCourse( void )
{
	printf("BBBB 修改课程\n");	
}

void F_NewCourse( void )
{
	printf("AAAA 新增课程\n");	
}

void F_PrintMenu( void )
{
	system("cls"); //清屏,该语句是否有效和操作系统有关 
	
	//输出课程信息管理系统的菜单命令选项(人机交互界面)
	printf("************************************************\n");
	printf("             欢迎使用<课程信息管理系统>\n");
	printf("************************************************\n");
	printf("[A] 新增课程\n");
	printf("[B] 修改课程\n");
	printf("[C] 删除课程\n");
	printf("[D] 显示所有课程列表\n");
	printf("[E] 查询学分在某区间的课程\n");
	printf("[F] 按代号查询课程\n");
	printf("[G] 按名称查询课程\n");
	printf("[X] 退出\n");
	printf("************************************************\n");
	printf("请输入要执行的功能命令前的字母(不区分大小写):");
}

/*****************************************
功能:接受用户输入命令字母
参数:无
返回:用户输入的命令字母 
*****************************************/
char F_GetCommand( void ); 
char F_GetCommand( void ){
	char CommandLetter;
	
	//用户输入命令字母, 转换成小写字母
	scanf("%c", &CommandLetter); 
	CommandLetter = tolower(CommandLetter);
	
	//清空键盘缓冲区 
	if(CommandLetter != 10){
		while( getchar() != 10 );
	}
	
	return CommandLetter;
}

/*****************************************
功能:执行用户输入的字母命令对应的操作(功能) 
参数:命令字母 
返回:void 
*****************************************/
void F_ExcuteCommand( char CommandLetter ); 
void F_ExcuteCommand( char CommandLetter ){
	//字母a-g、x是正确输入,其余的都不正确
	if(CommandLetter == 'a')		F_NewCourse();
	else if(CommandLetter == 'b')	F_UpdateCourse();
	else if(CommandLetter == 'c')	F_DeleteCourse();
	else if(CommandLetter == 'd')	F_ShowAllCourse();
	else if(CommandLetter == 'e')	F_QueryCourseByCredit();
	else if(CommandLetter == 'f')	F_QueryCourseByID();
	else if(CommandLetter == 'g')	F_QueryCourseByName();
	else if(CommandLetter == 'x')
	{
		printf("XXXX 退出\n");
		exit(0);
	}
	else printf("输入的命令错误\n");	
}

/*****************************************
功能:暂停,暂停等待用户键入回车
参数:void
返回:void 
*****************************************/
void F_Pause( void ); 
void F_Pause( void ){
	printf("回车返回菜单...");
	
	//接收输入的字符,清空键盘缓冲区 
	if(getchar() != 10){
		while( getchar() != 10 );
	}
}

int main(int argc, char** argv) {
	char CommandLetter; //命令 字母 
	 
	while(1){
		F_PrintMenu(); //输出菜单
		CommandLetter = F_GetCommand(); //用户输入命令字母
		F_ExcuteCommand(CommandLetter); //执行命令 
		F_Pause();	//暂停等待用户回车后再继续 
	} 

	return 0;
}

在这里插入图片描述

第四章 指针

1.指针的引入

C程序中 变量 到底是什么?

• 为了便于管理(分配和释放)内存被各种程序使用的 状况,操作系统以1字节为一个空间单位将内存按序编 了号(地址),内存地址编号为0~内存空间上限。

• 变量是存放某种类型的数据的内存单元的标识(名字) int Number;//定义一个存放int型数据的变量 Number是存放int型数据的内存单元的标识(名字)

• 一个变量(数据)占用的内存空间称为一个内存单元, 变量占用多少字节的空间由其存放的数据类型和操作 系统决定,例如在多数机器中,int类型的一个变量 占用4字节的内存空间(内存单元的大小为4字节)

• 可以使用 sizeof(数据类型) 获得一个该数据类型的 变量要占用几个字节的内存空间 • 可使用取址运算 &变量名 获得该变量占用的内存空 间的首个字节的地址,即入口地址,简称变量的地址

int a, b; //定义两个存放整型数据的内存单元

① 程序占用2个内存单元来存放两个int类型的数据

② 变量 a 和 b 是这两个内存单元的标识,表明这 两个内存单元的名字及其可存放的数据的类型

③ 直接使用变量名来存取其所指的内存单元 a = 10;//赋值(写数据到内存单元中) b = a; //先读出内存单元a中的数据, //然后把数据写入内存单元b中

④ 给一个语义清晰的变量名是为了方便阅读和理

如何表达和存放变量的地址?

如何通过变量的地址访问其所指的内存单元?

======》指针

在这里插入图片描述

在这里插入图片描述

2.指针初用

数据的两种方式:用变量名直接存取,用指针间接存取

#include <stdio.h>

int main(int argc, char *argv[]) {
	int Age; //年龄 
	int* p;	//指向存储整型数据的内存单元的地址变量 
	
	p = &Age;	//取址运算:把变量Age的地址赋值给p 
	printf( "变量Age的地址:%p\n", &Age ); //地址整数 
	printf( "变量p的地址值:%p\n", p ); //地址整数

	Age = 18; //直接把18写入内存单元Age中 
	*p = 20; //寻址运算:把20赋值给p指向的内存单元Age
	printf( "变量Age的值:%d\n", Age );
	printf( "*p(p指向的内存单元的值):%d\n", *p );
	
	return 0;
}

在这里插入图片描述

3.移址操作(偷梁换柱)

移址操作:改变指针的值,使指针指向的内存单元发生变化

• 地址向前移动n个单元: 地址表达式(地址或地址变量) + 整数n

• 地址向后移动n个单元: 地址表达式(地址或地址变量) - 整数n

#include <stdio.h>

int main(int argc, char *argv[])  {
	int a, b, c; 
	
	a = 5;
	b = 10;
	c = 15;

	*(&b - 1) = 0;	
	*(&b + 1) = 20;
	
	printf( "变量a的内存地址=%p  值=%d\n", &a, a );
	printf( "变量b的内存地址=%p  值=%d\n", &b, b );
	printf( "变量c的内存地址=%p  值=%d\n", &c, c );
	
	return 0;
}

使用移址、寻址操作改变了变量a、c的值

在这里插入图片描述

4.指针的指针

指向指针的指针:指向的内存单元存储的数据是地址

在这里插入图片描述

#include <stdio.h>

int main(int argc, char *argv[]) {
	int a = 10;
	int* p = &a;
	int** pp = &p;
	int*** ppp = &pp;

	printf( "     a = %d\n", a );
	printf( "    *p = %d\n", *p );
	printf( "  **pp = %d\n", **pp );
	printf( "***ppp = %d\n", ***ppp );
	printf( "\n" );
	printf( "   &a = %p\n", &a );
	printf( "    p = %p\n", p );
	printf( "  *pp = %p\n", *pp );
	printf( "**ppp = %p\n", **ppp );
	printf( "\n" );
	printf( "  &p = %p\n", &p );
	printf( "  pp = %p\n", pp );
	printf( "*ppp = %p\n", *ppp );
	
	return 0;
}

在这里插入图片描述

5.指针作为函数参数(交换人生)
#include <stdio.h>

//功能:交换两个变量的数据 
void Swap( int* pNum1, int* pNum2 ); 

void Swap( int* pNum1, int* pNum2 ){
	int Temp;
	
	Temp = *pNum1; //把pNum1指向内存单元中数据写到Temp内存单元 
	*pNum1 = *pNum2; //把pNum2指向内存单元中的数据写到pNum1指向内存单元 
	*pNum2 = Temp; //把Temp内存单元数据写到pNum2指向内存单元 
}

int main(int argc, char *argv[]) {
	int a = 10;
	int b = 5; 
	
	printf("交换前:a = %d\tb = %d\n", a, b );
	
	交换两个变量的数据
	Swap( &a, &b );  //给的参数分别是变量a和b的地址

	printf("交换后:a = %d\tb = %d\n", a, b );
	
	return 0;
}

在这里插入图片描述

6.移址寻址简化写法

移址寻址操作*(p+n)简化写法p[n] :从p给出的地址移动n个单元处的数据

#include <stdio.h>

int main(int argc, char *argv[])  {
	int a, b; 
	char* pChar; //指向字符内存单元的指针
	int k;
	 
	pChar = (char*)(&b);

	a = 0x48474645;
	b = 0x44434241;
	
	for(k = 0; k < 8; k++){
		printf("%c", *(pChar+k));
	}
	printf("\n"); 
	
	//移植寻址操作 *(p+k) 简化写法 p[n] 
	for(k = 0; k < 8; k++){
		printf("%c", pChar[k]);
	}
	printf("\n"); 

	return 0;
}

在这里插入图片描述

#include <stdio.h>

int main(int argc, char *argv[])  {
	int s0, s1, s2, s3;  //四个学生成绩 
	int* p;	 
	int k;
	 
	p = &s3;
    //*(p+0)=60
	p[0] = 60;
	p[1] = 70;
	p[2] = 80;
	p[3] = 90;
	
	for(k = 0; k < 4; k++){
		printf("%d\n", p[k]);
	}

	return 0;
}

在这里插入图片描述

第五章 数组

在这里插入图片描述

C程序如何表示和处理集合数据? ==》(数组)

1.集合概念

集合:性质(数据类型)相同的一组对象或数据

• 若某一集合记为S,则其元素可分别记为e1 … en ,记为 S = {e1 , e2 , ……, en } 集合S有n个元素(集合的势为n 或 长度为n) 1,2,…,n 是集合中各元素的标号,也可以说是其ID (identifier,唯一标识)

• 从1开始数数是人的习惯,实际上任何进制系统都是从0 开始计数的,在C等程序设计语言中,从0开始计数 有n个元素集合 S = {a0 , a1 , ……, an-1 } 0,1,2,…,n-1是长度为n的集合中各元素的标号

• 任何集合都可以用矩阵来表示 矩阵的形态视具体问题和求解方法而定

2.一维数组

用一维矩阵(向量)表达元素个数确定的集合

2.1问题一:数组倒置

任务:管理某门课N个学生的成绩

• 输入N个学生的成绩

• 按输入顺序倒序输出成绩(最后 输入的先输出)

• 排序,以便按某种顺序输出成绩

• 统计平均成绩、最高分、最低分、 各分数段的人数

目前学过的技术能实现上述任务吗? 除非人数是固定的,否则目前学过的 技术无法完成倒序输出和排序任务! 必须将输入的N个学生成绩存放在N个 内存单元中,才能实现倒序和排序

数组:按顺序存放N个同类型数据的 内存块(内存空间)。

上述任务实现:输入N个学生的成绩,存在数组中,倒序输出成绩


#include <stdio.h>

int main(int argc, char *argv[]) {
	int Score[5]; //存放5个学生成绩的数组 
	int k; //循环变量,数组元素的下标 

	//循环输入5个学生的成绩
	for( k = 0; k < 5; k++ ) {
		printf( "请输入第 %d 个成绩:", k );
		scanf( "%d", Score+k ); //Score+k是第个元素的地址 
	}
	
	//倒着输出5个学生的成绩
	printf( "倒着输出学生的成绩:\n" );
	for( k = 4; k >= 0; k-- ) {
		printf( "%d  ", Score[k] );
	}	
	
	return 0;
}

数组名是什么?数组名的值是数组占用内存空间的入口地址

定义数组时给数组元素初值 :

• 给出每一个元素的初值 int Score[5] = {90, 80, 70, 60, 50} ;

• 给出前面几个元素的初值,后面元素自动为 0, int Score[5] = {90, 80} ;

• 给出所有元素的初值,省略数组长度 (不好 ) int Score[] = {90, 80, 70, 60, 50} ;

程序运行时,不能一次给数组的各 个元素赋值,只能给元素赋值! 例如 Score[0] = 99;

**注意:**遍历长度为 N的数组各个元素

//正向遍历:从第 0 号元素开始遍历 for( i = 0; i < N; i++) { 处理数组第 i个元素相关代码 } 或

//反向遍历:从第 N -1 号元素开始遍历 for( i = N – 1; i >= 0; i–){ 处理数组第 i个元素相关代码 }

符号常量:用符号代表一个常量(不变的数据)

数组的长度会多次出现在代码中,如 果数组的长度变了,则每个地方都要 修改才能保证程序正确。很难保证! 怎么办?

通常,在程序开始处定义一个符号常量来表示数组的 长度,代码中用到数组长度的地方全部用该符号常量 替代。如果数组长度变了,只需修改符号常量表示的 数据,这样容易保证程序正确。程序的可维护性!

在这里插入图片描述

下面展示利用键盘输入缓冲区机制:连续输入 多个数据,数据之间用空格或换行符 用空格分开

#include <stdio.h>

#define STD_NUM 10 //学生人数 

int main(int argc, char *argv[]) {
	float Score[STD_NUM]; //STD_NUM个学生成绩
	int k; //循环变量,数组元素的下标 

	//输入样例:95.0 88.0 84.5 79.0 76.5 74.0 71.0 68.0 65.0 50.0
	printf( "请输入 %d 个成绩:\n", STD_NUM );
	for( k = 0; k < STD_NUM; k++ ) {
		scanf( "%f", &Score[k] ); 
	}
	
	//倒着输出5个学生的成绩
	printf( "倒着输出学生的成绩:\n" );
	for( k = STD_NUM - 1; k >= 0; k-- ) {
		printf( "%.1f ", Score[k] );
	}	
	printf( "\n" ); 
	
	return 0;
}

在这里插入图片描述

2.2问题二:数组计算

输入(给定)N个学生的成绩,统计总分、平均分、最高分、最低分、各分数段的人数

#include <stdio.h>

#define STD_NUM 10 //学生人数 

int main(int argc, char *argv[]) {
	float Score[STD_NUM]; //STD_NUM个学生成绩
	int k; //循环变量,数组元素的下标 
	float Sum, Max, Min; //总分、最高分、最低分 
	//不同分数段的人数:下标0为[0,10)的......  
	int NumLevel[11] = {0}; //初始为0 

	printf( "请输入 %d 个成绩:\n", STD_NUM );
	for( k = 0; k < STD_NUM; k++ ) {
		scanf( "%f", &Score[k] ); 
	}
	
	//统计总分、最高分、最低分、各分数段人数
	Sum = 0; //总分初始值 
	Max = Score[0];  //最高分初始值 
	Min = Score[0]; //最低分初始值 
	for( k = 0; k < STD_NUM; k++ ) {
		Sum += Score[k]; //把第k个成绩加入其中总分 
		
		if ( Score[k] > Max ) Max = Score[k]; //最高分 
		if ( Score[k] < Min ) Min = Score[k]; //最低分
		
		NumLevel[ ( (int)Score[k] ) / 10 ] ++;
	}

	//输出结果 
	printf( "总分:%.1f\n", Sum );
	printf( "平均分:%.1f\n", Sum / STD_NUM );
	printf( "最高分:%.1f\n", Max); 
	printf( "最低分:%.1f\n", Min ); 
	printf( "优秀:%d 人\n", NumLevel[10] + NumLevel[9] ); 
	printf( "良好:%d 人\n", NumLevel[8] ); 
	printf( "中等:%d 人\n", NumLevel[7] ); 
	printf( "及格:%d 人\n", NumLevel[6] ); 
	printf( "不及格:%d 人\n", 
	  NumLevel[5] + NumLevel[4] + NumLevel[3] 
	  + NumLevel[2] + NumLevel[1] + NumLevel[0] ); 
	
	return 0;
}

在这里插入图片描述

3.移址寻址访问数组元素

访问数组元素的另一种方法:用指针移址、寻址操作访问数组元素


#include <stdio.h>

int main(int argc, char *argv[]) {
	float score[5] = {90, 80, 70,60,50};
	int i;
	
	//输出score的值(地址),即数组的入口地址 
	printf("score = %p\n", score); 
	//取址操作:输出各个元素的地址 
	for(i = 0; i < 5; i++){
		printf("&score[%d] = %p\n", i, &score[i]); 
	}
	
	//移址运算输出各个元素的地址
	for(i = 0; i < 5; i++)	{
		printf("score+%d = %p\n", i, score+i); 
	}
	
	//寻址运算输出各个元素的值 
	printf("*score = %f\n", *score);
	for(i = 0; i < 5; i++)	{
		printf("*(score+%d) = %f\n", i, *(score+i) ); 
	}
	
	return 0;
}


#include <stdio.h>

int main(int argc, char *argv[]) {
	float Score[5] = {90, 80, 70, 60, 50};
	int i;
	float* p;  //指向的内存单元存放float类型数据 
	
	p = Score; //p指向数组入口,Score的值是固定的 

	//用指针遍历数组的元素(地址和值) 
	for(i = 0; i < 5; i++){
		printf("p+%d = %p\t", i, p + i ); 
		printf("*(p+%d) = %f ", i, *(p+i) ); 
		printf("*(p+%d) = %f\n", i, p[i] );
	}
	
	return 0;
}

在这里插入图片描述

4.冒泡排序

排序(冒泡法):输入N个学生的成绩,对成绩升序排序,按序输出成绩

在这里插入图片描述

#include <stdio.h>

#define STD_NUM 10 //10个学生 

int main(int argc, char *argv[]) {
	float Score[ STD_NUM ]; //STD_NUM个学生成绩
	int k; //循环变量,数组元素的下标 
	float temp; //临时用 
	int NeedSort; //数组中前面待排序的数据个数 

	printf( "请输入 %d 个成绩:\n", STD_NUM );
	for( k = 0; k < STD_NUM; k++ ) {
		scanf( "%f", &Score[k] ); 
	}
	
	//冒泡排序(升序:从小到大) 
	NeedSort = STD_NUM;
	while( NeedSort > 1 ){ //待排序者至少2个
		//把待排序数据中最大者写到最后一个元素中
		for( k = 0; k < (NeedSort - 1); k++ ){
			//若a[k] > a[k+1],交换两者
			if( Score[k] > Score[k+1]){
				temp = Score[k];
				Score[k] = Score[k+1];
				Score[k+1] = temp;
			}
		}

		NeedSort--;//待排序的数据个数减1
	}	 
	
	//按降序输出数据(反向输出数据)
	printf( "按降序输出成绩:\n" );
	for( k = STD_NUM - 1; k >= 0; k-- ) {
		printf( "%.1f ", Score[k] ); 
	}
	printf( "\n" );
	
	return 0;
}

在这里插入图片描述

**加大难度:**按顺序输入学号及其成绩,对成绩排序,输出成绩及其对应的学号

前面的程序存在的问题: 没有考虑成绩和学号的对 应关系,不知道每一个成 绩是哪个学生的 怎么解决这个问题?

解决方案:

• 和成绩一样,N个学生的 学号也是一个数据集,因 此,需要定义两个数组, 分别存放学号和成绩

• 学号和成绩数组中下标相 同的元素是同一学生的

• 输入和排序时,要保证同 一学生的学号和成绩一一 对应,若某个成绩的存放 位置变了,则对应学号的 存放位置也要更改

/*输入样例:
1 88.0
2 95.0
3 84.5
4 65.0
5 50.0
*/

#include <stdio.h>

//学生人数  
#define STD_NUM 5 

/********************************************
功能:正序输入学号、成绩
参数:成绩数组入口地址,学号数组入口地址,数组长度 
返回:void 
********************************************/ 
void F_Input( float* pScore, int* pStdNo, int Length );
void F_Input( float* pScore, int* pStdNo, int Length ){
	printf( "输入格式:\n学号【空格】成绩【回车】\n");
	printf( "输入样例:\n2 98.5【回车】\n");
	printf( "输入%d个学号和成绩:\n", Length);
	for( ; Length > 0; Length-- ) {
		scanf( "%d%f", pStdNo, pScore );
		pStdNo++; //指向下一个学号内存单元	
		pScore++; //指向下一个成绩内存单元 
	}
} 

/************************************************************
功能:反向输出成绩及其对应的学号 
参数:成绩数组入口地址,学号数组入口地址,数组长度 
返回值:无 
*************************************************************/
void F_PrintReverse( float* pScore, int* pStdNo, int Length );
void F_PrintReverse( float* pScore, int* pStdNo, int Length ){
	int k;
	
	printf( "%-6s %-6s\n", "学号", "成绩" );
	for( k = Length - 1; k >= 0; k-- ) {
		printf( "%-6d %-6.1f\n", pStdNo[k], pScore[k] ); 
	}
}

/************************************************************
功能:正向输出成绩及其对应的学号
参数:成绩数组入口地址,学号数组入口地址,数组长度 
返回值:无 
*************************************************************/
void F_PrintForward( float* pScore, int* pStdNo, int Length );
void F_PrintForward( float* pScore, int* pStdNo, int Length ){
	int k;
	
	printf( "%-6s %-6s\n", "学号", "成绩" );
	for( k = 0; k < Length; k++ ) {
		printf( "%-6d %-6.1f\n", pStdNo[k], pScore[k] ); 
	}
}

/*************************************************************
功能:对成绩数组升序排序,同时保证成绩与学号的对应关系 
参数:成绩数组(入口地址),学号数组(入口地址),数组长度 
返回值:无 
*************************************************************/
void F_SortBubble( float* pScore, int* pStdNo, int Length );
void F_SortBubble( float* pScore, int* pStdNo, int Length ){ 
	float temp; //临时用 
	int NeedSort; //数组中前面待排序的数据个数 
	int k; //循环变量,数组元素的下标 
	int TempInt;

	//排序(升序:从小到大) 
	for( NeedSort = Length; NeedSort > 1; NeedSort-- ) { 
		//把待排序数据中最大者写到最后一个元素中
		for( k = 0; k < (NeedSort - 1); k++ ){
			//若Score[k] > Score[k+1],交换两者
			if( pScore[k] > pScore[k+1]){
				//交换成绩 
				temp = pScore[k];
				pScore[k] = pScore[k+1];
				pScore[k+1] = temp;
				
				//交换对应的学号 
				TempInt = pStdNo[k];
				pStdNo[k] = pStdNo[k+1];
				pStdNo[k+1] = TempInt;
			}
		}
	}	 
} 

int main(int argc, char *argv[]) {
	int StdNo[STD_NUM]; //STD_NUM个学生的学号
	float Score[ STD_NUM ]; //STD_NUM个学生的成绩
	
	//输入学号和成绩
	F_Input( Score, StdNo, STD_NUM );  

	//排序前 
	printf( "***排序前***\n" );
	//倒序输出学生成绩 
	F_PrintReverse( Score, StdNo, STD_NUM ); 

	//调用自定义函数排序
	F_SortBubble( Score, StdNo, STD_NUM ); 	
	
	//排序后 
	printf( "***排序后***\n" );
	//倒序输出学生成绩 
	F_PrintReverse( Score, StdNo, STD_NUM ); 
	
	return 0;
}

在这里插入图片描述

**梅开二度,再上力度:**管理M个学生N门课的成绩:如何用一维数组存储、排序和检索?

**梅开三度:**按课程对成绩排序,怎么做?

是否感到有点吃力??哈哈 !有好的方案没有?

5.二维数组

表达 m x n 矩阵,方便、清晰明了

在这里插入图片描述

5.1.按行输出矩阵
#include <stdio.h>

#define ROW_NUM 5
#define COL_NUM 3 

int main(int argc, char *argv[]) {
	int a[ROW_NUM][COL_NUM] = {
		11, 12, 13, 
		21, 22, 23, 
		31, 32, 33, 
		41, 42, 43, 
		51, 52, 53 
	}; 
	int i, j; //i行号,j列号 
	
	//按行输出 (先行后列)
	for ( i = 0; i < ROW_NUM; i++ ) {
		//处理第i行的数据 
		for( j = 0; j < COL_NUM; j++ ) {
			//处理第i行第j列数据 
			printf( "%3d", a[i][j] );
		}
		printf( "\n" ); //一行输出后换行 
	}
	
	return 0;
}

在这里插入图片描述

5.2.按列输出矩阵(矩阵转置)
#include <stdio.h>

#define ROW_NUM 5
#define COL_NUM 3 

int main(int argc, char *argv[]) {
	int a[ROW_NUM][COL_NUM] = {
		11, 12, 13, 
		21, 22, 23, 
		31, 32, 33, 
		41, 42, 43, 
		51, 52, 53 
	}; 
	int i, j; //i行号,j列号 
	
	//按列输出(先列后行) 
	for( j = 0; j < COL_NUM; j++ ) {
		//处理第就j列数据 
		for ( i = 0; i < ROW_NUM; i++ ) {
			//处理第j列第i行数据 
			printf( "%3d", a[i][j] );
		}
		printf( "\n" ); //列输出后换行 
	}
	
	return 0;
}

在这里插入图片描述

5.3.矩阵转置后复制给另一个矩阵

#include <stdio.h>

#define M 5
#define N 3 

int main(int argc, char *argv[]) {
	int a[M][N] = {
		11, 12, 13, 
		21, 22, 23, 
		31, 32, 33, 
		41, 42, 43, 
		51, 52, 53 
	}; 
	int b[N][M]; 
	int i, j; 
	
	//把数组a转置后给数组b,即b[j][i] = a[i][j] 
	for ( i = 0; i < M; i++ ) {
		for( j = 0; j < N; j++ ) {
			b[j][i] = a[i][j];
		}
	}
	
	//输出矩阵b
	for( j = 0; j < N; j++ ) {
		for ( i = 0; i < M; i++ ) {
			printf( "%3d", b[j][i] );
		}
		printf( "\n" ); 
	}
	
	return 0;
}

在这里插入图片描述

5.4.计算nxn矩阵对角线元素之和
#include <stdio.h>

#define N 3 

int main(int argc, char *argv[]) {
	int a[N][N] = {
		11, 12, 13, 
		21, 22, 23, 
		31, 32, 33
	}; 
	int i; 
	int sum; 
	
	//计算对角线元素的和 
	sum = 0;
	for ( i = 0; i < N; i++ ) {
		sum += a[i][i];
	}
	
	printf( "对角线元素的和为 %d\n", sum ); 
	
	return 0;
}

5.5管理M位学生N门课的成绩:用二维数组

由框架到细节,由简单到复杂,从核心功能开始,把相对独立的功能模块化,逐步完成

① 定义学生人数和课程门数符号常量;定义成绩 数组,给初始值(便于测试);输出成绩(每 个学生的成绩在一行中输出)

② 计算每个学生的总分和平均分;为了以后排序 时使用,把总分和平均分也存在成绩数组中 (当作两门课程的成绩看待)

③ 增加学号数组,定义时给初值(便于测试); 修改输出成绩单的代码,每一行先输出学号

④ 修改输出代码:增加表头和表尾

⑤ 继续增加其它功能……

#include <stdio.h>

#define STD_NUM 5     //学生人数 
#define COURSE_NUM 5  //3门课、总分、平均分 
/*
1001 11.0 22.0 33.0
1002 22.0 33.0 44.0
1003 33.0 44.0 55.0
1004 44.0 55.0 66.0
1005 55.0 66.0 77.0
*/


/******************************************************
功能:输入学号及其COURSE_NUM门课程的成绩 
参数:成绩数组,学号数组 
返回值:无 
******************************************************/
void F_Input( float Score[][COURSE_NUM], int StdNo[] );
void F_Input( float Score[][COURSE_NUM], int StdNo[] ){
	int i, j; //第i位学生,第j门课
	int CourseNum = COURSE_NUM - 2; //需要输入成绩的课程门数 

	printf( "每一行输入:学号 %d门课的成绩,例如\n", CourseNum );
	printf( "201601 98.5 89.5 78.0\n" );
	printf( "数据之间用空格分开,每一行输完后回车\n" );
	printf( "请按上述要求输入 %d 行数据:\n", STD_NUM );
	for ( i = 0; i < STD_NUM; i++ ) {
		scanf( "%d", &StdNo[i] ); //学号 
		for( j = 0; j < CourseNum; j++ ) {
			scanf( "%f", &Score[i][j] ); //成绩 
		}
	}
}

/*****************************************
功能:计算每个学生的总分和平均分
参数:成绩数组(学生人数和课程门数是固定的) 
返回值:无 
*****************************************/
void F_Sum( float Score[STD_NUM][COURSE_NUM] );
void F_Sum( float Score[STD_NUM][COURSE_NUM] ){
	int i, j; //第i位学生,第j门课
	float sum; //第i位学生的总分 

	for ( i = 0; i < STD_NUM; i++ ) {
		sum = 0; //第i位学生的总分初始为0 
		for( j = 0; j < COURSE_NUM - 2; j++ ) {
			sum += Score[i][j]; //第j门课成绩计入总分 
		}
		
		//更新第i位学生的总分和平均分 
		Score[i][COURSE_NUM - 2] = sum; //更新总分
		sum /= (COURSE_NUM - 2); //平均分 
		Score[i][COURSE_NUM - 1] = sum; //更新平均分 
	}
} 

/*****************************************
功能:输出成绩单 
参数:成绩数组,学号数组 
返回值:无 
*****************************************/
void F_PrintReport( float Score[][COURSE_NUM], int StdNo[] );
void F_PrintReport( float Score[][COURSE_NUM], int StdNo[] ){
	int i, j; //第i位学生,第j门课
	float sum[COURSE_NUM] = {0.0}; //COURSE_NUM门课程的总分 

	//输出表头 
	printf( "------------------------------------------------\n" );
	printf( "                  学生成绩单\n" );
	printf( "------------------------------------------------\n" );
	printf( "%-8s%8s%8s", "学号", "语文", "数学" );
	printf( "%8s%8s%8s\n", "地理", "总分", "平均分" );
	printf( "------------------------------------------------\n" );

	//输出学号、成绩,统计各门课总分 
	for ( i = 0; i < STD_NUM; i++ ) {
		printf( "%-8d", StdNo[i] );//第i个学生的学号 
		for( j = 0; j < COURSE_NUM; j++ ) {
			printf( "%8.1f", Score[i][j] );
			sum[j] += Score[i][j]; //把第j门课成绩加到总分 
		}
		printf( "\n" ); //输出一位学生的成绩后换行 
	}

	//输出表尾 
	printf( "------------------------------------------------\n" );
	printf( "%-8s", "总平均分" );
	for( j = 0; j < COURSE_NUM; j++ ) {
		printf( "%8.1f", sum[j] / STD_NUM );
	}
	printf( "\n" ); //输出一位学生的成绩后换行
}

int main(int argc, char *argv[]) {
	//每行前3个是3门课的成绩,后2个是总分和平均分 
	float Score[STD_NUM][COURSE_NUM] = {0};
	int StdNo[STD_NUM] = {0}; //学号数组 
	
	F_Input( Score, StdNo );//输入学号和成绩 
	system( "cls" ); //清屏 
	F_Sum( Score );//计算并更新总分和平均分 
	F_PrintReport( Score, StdNo );//输出成绩单 
	
	return 0;
}

在这里插入图片描述

6.用指针访问二维数组的元素

在这里插入图片描述

#include <stdio.h>

int main(int argc, char *argv[])  {
	float Score[5][3] = {
		99, 88, 77,
		98, 87, 76,
		97, 86, 75,
		95, 84, 73,
	};
	int i, j; //i行,j列 
	
	//输出数组、各行和各元素的入口地址 
	printf( "Score:\t\t%p\n", Score ); //数组入口地址 
	for( i = 0; i < 5; i++ ){
		//数组第i行入口地址 
		printf( "Score[%d]:\t%p\n", i, Score[i] ); 
		printf( "Score+%d:\t%p\n", i, Score + i ); 
		
		//第i行各列(元素)的地址 
		for( j = 0; j < 3; j++ ){
			//数组第i行第j列元素的入口地址
			printf( "Score[%d]+%d:\t%p\t", i, j, Score[i]+j ); 

			//数组第i行第j列元素值 
			printf( "Score[%d][%d]:%.1f\t", i, j, Score[i][j] ); 
			printf( "*(Score[%d]+%d):%.1f\n", i, j, *(Score[i]+j) ); 
		}
	}	
	
	return 0; 
}

在这里插入图片描述


#include <stdio.h>
int main(int argc, char *argv[]) {
	float Score[5][3] = {
		99, 88, 77,
		98, 87, 76,
		97, 86, 75,
		95, 84, 73,
	};
	int i, j; //i行,j列 
	float* pFloat;  //指向的内存单元存放float类型数据
	
	pFloat = &Score[0][0]; //p指向数组入口
	
	//输出数组、各行和各元素的入口地址 
	printf( "数组入口地址:\t%p\n", pFloat ); //数组入口地址 
	for( i = 0; i < 5; i++ ){
		// 处理第i行的数据 
		for( j = 0; j < 3; j++ ){
			//数组第i行第j列元素的入口地址
			printf( "%d行%d列的地址:\t%p\t", i, j, pFloat ); 

			//数组第i行第j列元素值 
			printf( "值:%.1f\n", *pFloat ); 
			
			pFloat++; //指向下一个内存单元 
		}
	}	
	return 0;
}

#include <stdio.h>

int main(int argc, char *argv[]) {
	float Score[5][3] = {
		99, 88, 77,
		98, 87, 76,
		97, 86, 75,
		95, 84, 73,
	};
	int k;
	float* pFloat;  //指向的内存单元存放double类型数据
	
	pFloat = &Score[0][0]; //p指向数组入口
	
	//把二维数组当作一维数组处理,按元素存储顺序访问 
	for( k = 0; k < (5*3); k++, pFloat++ ){
		printf( "p:%p\t", pFloat ); 
		printf( "*p:%.1f\n", *pFloat ); 
	}	
	
	return 0;
}

第六章 字符串

要解决的问题

在这里插入图片描述

在这里插入图片描述

基本操作

1.用各种方式输出字符

#include <stdio.h>

int main(int argc, char *argv[])
{
	printf("abcd ABCD\n");	//直接用字符或转义符 
	printf("\x61\x62\x63\x64\x20\x41\x42\x43\x44\x0A");	//十六进制字符编码 
	printf("\141\142\143\144\040\101\102\103\104\012");	//八进制 字符编码

	printf("01289\n");	//直接用字符或转义符 
	printf("\x30\x31\x32\x38\x39\x0A");	//十六进制字符编码 
	printf("\060\061\062\070\071\012");	//八进制 字符编码

	//用占位符输出字符
	printf("%c%c%c%c", '0', '1', '2', '\n');	//直接用字符或转义符 
	printf("%c%c%c%c", '\x30', '\x31', '\x32', '\x0A');//十六进制字符编码 
	printf("%c%c%c%c", '\060', '\061', '\062', '\012');//八进制 字符编码
	printf("%c%c%c%c", 48, 49, 50, 13);//十进制编码值
	
	//混合输出
	printf("a%cb%cc%cd%ce%c", '0', '\x31', '\062', 52, '\n');
	
	return 0;
}

在这里插入图片描述

2.输出ASCII字符编码表

#include <stdio.h>

int main(int argc, char *argv[])
{
	int k;
	
	//用ASCII字符集编码输出ASCII字符集 
	printf("ASCII字符集\n");
	printf("进制D\t进制H\t进制O\t字符\n");
	for(k = 0; k < 128; k++)
	{
		printf("%d\t%x\t%o\t%c\n", k, k, k, k);   
	}
	
	return 0;
}

在这里插入图片描述

3.字符运算

内存中存放的数据,完全由读成什么类型的数据决定 的,若以字符(字节)来读,则就是字符(整数)

#include <stdio.h>

int main(int argc, char *argv[])
{
	char c1;	//字符变量 

	printf("请输入一个小写字母:");
	scanf("%c", &c1);

	//小写字母及其写字母的字符编码值相差32 
	printf("%c\n", c1-32);	
	
	return 0;
}

在这里插入图片描述

在这里插入图片描述

4.字符串占位符

用字符串占位符 %s 输出字符串常量(字符串结束符号’\0’ 输出遍历字符串时读到字符’\0’就结束)

如所给字符串常量尾部没有’\0’,编译器在编译时会自定加上结束字符’\0’ 字符串常量 “abc” 在内存中是 ‘a’‘b’‘c’‘\0’


#include <stdio.h>
int main(int argc, char *argv[]){
	printf("%s\n", "Hello world!");
	printf("%s\n", "Hello\0 world!");
	printf("%s-%s\n", "abc\0def", "abcdef");
	
	return 0;
}

在这里插入图片描述

5.用字符数组表达字符串

定义可写字符串(字符数组),遍历字符串,输出字符串

#include <stdio.h>
int main(int argc, char *argv[]){
	char str[10]; //定义字符数组(可变字符串)
	int i; 

	for(i = 0; i < 10; i++)	{
		//给可变字符串元素赋值(字符编码值) 
		str[i] = i + 65; 
	}
	printf("%s\n", str);//输出字符串 
	
	for(i = 0; i < 10; i++)	{
		str[i] += 32; 
	}
	printf("%s\n", str);//输出字符串 

	return 0;
}

在这里插入图片描述

字符数组的值显示在""中,是可写内容字符串

★可写字符串中必须明 确给出结束字符 ’ \0

#include <stdio.h>
int main(int argc, char *argv[]){
	char str[10]; //定义字符数组(可变字符串)
	int i; 

	//最后一个元素是结束字符 
	str[9] = '\0';
	
	for(i = 0; i < 9; i++)	{
		//给可变字符串元素赋值(字符编码值) 
		str[i] = i + 65; 
	}
	printf("%s\n", str);//输出字符串 

	return 0;
}

在这里插入图片描述

6.定义可写字符串(字符数组)时赋初值

#include <stdio.h>
int main(int argc, char *argv[]){
	//用集合元素的方式给字符数组初值。麻烦! 
	char str[5] = {'A', 'B', 'C', 'D', '\0'};

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

	return 0;
}
#include <stdio.h>
int main(int argc, char *argv[]){
	//字符串常量作为字符数组的初值!简! 
	char str[5] = "ABCD\0";  

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

	return 0;
}

在这里插入图片描述

7.遍历字符串

#include <stdio.h>
int main(int argc, char *argv[]){
	char str[5] = "ABCD\0";  
	int k;
	
	//固定长度的方式,除非特殊需要,否则不好! 
	for(k = 0; k < 5; k++){
		printf("%c", str[k]);
	}

	return 0;
}

//读到/0停止
#include <stdio.h>
int main(int argc, char *argv[]){
	char str[5] = "ABCD\0";  
	int k = 0;
	
	//读到结束符'\0'停止循环 
	while( str[k] != '\0' ){
		printf("%c", str[k]);
		k++;
	}

	return 0;
}

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]){
	char str[5] = "ABCD\0";  
	int k, Length;
	
	//按字符串实际长度遍历 
	Length = strlen(str); //获取字符串长度 
	for(k = 0; k < Length; k++)	{
		printf("%c", str[k]);
	}

	return 0;
}

在这里插入图片描述

8.统计文本中各种各字母出现的次数


#include <stdio.h>
#include <ctype.h>

int main(int argc, char *argv[]){
	char str[] = "Everyone has their own dreams.\0";
	int LetterNum[26] = {0};//26个字母出现的个数
	int k;
	char c;
	
	//统计字符串中各字母出现的次数 
	for(k = 0; str[k] != '\0'; k++ ){
		c = toupper(str[k]); //读取第k个字符,转换为大写 
		if( c > 64 && c < 91 ) LetterNum[c % 65]++; 
	}
	
	//输出26个字母出现的次数
	for(k = 0; k < 26; k++ ){
		printf("%c:%d\n", k + 65, LetterNum[k]);
	}
	
	return 0;
}

9.统计文本中各种各字母出现的次数(2)

用字符指针存放字符串入口地址:遍历字符串

在这里插入图片描述

#include <stdio.h>
#include <ctype.h>

int main(int argc, char *argv[]){
	char* pStrConst = "Everyone has their own dreams.\0";
	int LetterNum[26] = {0};//26个字母出现的个数
	int k;
	char c;
	
	//统计字符串中各字母出现的次数 
	for(k = 0; pStrConst[k] != '\0'; k++ ){
		c = toupper(pStrConst[k]); //读取第k个字符,转换为大写 
		if( c > 64 && c < 91 ) LetterNum[c % 65]++; 
	}
	
	//输出26个字母出现的次数
	for(k = 0; k < 26; k++ ){
		printf("%c:%d\n", k + 65, LetterNum[k]);
	}
	
	return 0;
}

说明:数组名本质上是一个指向数组 内存块的指针(地址变量), 用 数组名[标号] 来访问数组 对应标号的元素本质上是通过 移址+寻址操作来访问元素

指向数组入口地址的指针和数 组名本质上相同,因此,也可 以用 指针名[标号]来访问元素

实际上,编译器把源代码编译 为可执行的机器指令码时,任 何数据的存取操作都处理为通 过移址+寻址操作来完成

10.字符串处理函数

字符串处理函数:要正确使用两类字符指针参数
在这里插入图片描述

函数的具体使用方法:参考手册或网络资源(网络资源的利用非常重要!

★调用字符串函数时,要正确地给出字符 指针参数的值:const修饰的指针参数, 调用时参数值(地址)指向的内存只要可读 即可;无const修饰的指针参数,调用时 参数值(地址)指向的内存必须是可写的

#include <stdlib.h>

double atof( const char *str );

功能:将字符串str转换成一个双精度数值 并返回结果。参数str必须以有效数字开 头,但是允许以"E"或"e"除外的任意非 数字字符结尾。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	char str[10] = "22.22ABC\0";
	char *pcStr = "33.33ABC\0";

	printf("%Lf\n", atof( "11.11ABC" ) ) ;
	printf("%Lf\n", atof( str ) ) ;
	printf("%Lf\n", atof( pcStr ) ) ;
	
	return 0;
}

在这里插入图片描述

#include <string.h>

void* memchr(

​ const void *buffer //可读 , int ch , size_t count

);

• 功能:在buffer指向的大小为 count个字符的内存块中查找 给定字符 ch 首次出现的位置

• 返回:指针(内存地址),指向给定字符 ch 在内存块中首 次出现的位置(地址);如果没有找到,返回NULL (0 或 ’ \0’ )

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

int main(int argc, char *argv[])
{
	char str[] = "ABCDE\0";
	char* pcStr = "ABCDE\0";
	
    if( memchr("ABCDE", 'C', 5) == NULL )
		printf( "没找到C\n" );
    else  printf( "找到了C\n" );
      
    if( memchr(str, 'C', strlen(str)) == NULL )
		printf( "没找到C\n" );
    else  printf( "找到了C\n" );

    if( memchr(pcStr, 'C', strlen(pcStr)) == NULL )
		printf( "没找到C\n" );
    else  printf( "找到了C\n" );
      
	return 0;
}

#include <string.h>

void *memcpy(

​ void *to //要可写 , const void *from , size_t count

);

• 功能:从指针 from 指 向的内存中读取 count 个字符,写入指针 to 指向的内存中

• 返回:to指针

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

int main(int argc, char *argv[])
{
	char* pcStr = "ABCDE";
	char str[] = "ZZZZZ";
	
	printf("复制前:%s\n", str); 

	memcpy(str, pcStr, strlen(pcStr) );
	
	printf("复制后:%s\n", str);

	return 0;
}

在这里插入图片描述

第七章 自定义数据类型

1.定义结构体类型

定义结构体类型(对象类型)表达学生成绩(一行一个对象)

//定义学生成绩结构体类型

struct ST_score{

​ int Num; //学号

​ float Grade[5]; //5个成绩

};

//给自定义的数据类型 struct ST_score 定义一个别名

typedef struct ST_score TScore;

//程序中使用自定义的数据类型

TScore SC1;变量,存放某个学生及其成绩

TScore Score[5]; //5个学生及其成绩

Score[0].Num = 201601; //第0个学生的学号

Score[0].Grade[0] = 90.0; //第0个学生的第0门课程成绩

2.案例通过结构体逐步优化

2.1一个学生3门课程成绩
#include <stdio.h>

//定义学生成绩结构体类型
struct ST_score{
    int Num;        //学号(属性或字段) 
    float Grade[5]; //5个成绩(属性或字段)
};
//给自定义的数据类型 struct ST_score 定义一个别名
typedef struct ST_score TScore; 


int main(int argc, char *argv[]){
	TScore S; //定义一个学生成绩对象(变量)
	
	//给对象的属性赋值 
	S.Num = 201601;    //学生成绩对象.学号 
	S.Grade[0] = 90.0; //学生成绩对象.语文成绩 
	S.Grade[1] = 87.5; //学生成绩对象.数学成绩 
	S.Grade[2] = 98.0; //学生成绩对象.英语成绩 
	S.Grade[3] = S.Grade[0] + S.Grade[1] + S.Grade[2]; //总分
	S.Grade[4] = S.Grade[3] / 3.0; //平均分 

	// 输出学生成绩对象S的属性
	printf("学生成绩:\n"); 
	printf("学号:%d\n", S.Num);
	printf("语文:%.1f\n", S.Grade[0]);
	printf("数学:%.1f\n", S.Grade[1]);
	printf("英语:%.1f\n", S.Grade[2]);
	printf("总分:%.1f\n", S.Grade[3]);
	printf("平均:%.1f\n", S.Grade[4]);
	
	return 0;
}

在这里插入图片描述

2.2一个学生3门课程成绩:优化函数
#include <stdio.h>

//定义学生成绩结构体类型
struct ST_score{
    int Num;        //学号(属性或字段) 
    float Grade[5]; //5个成绩(属性或字段)
};
//给自定义的数据类型 struct ST_score 定义一个别名
typedef struct ST_score TScore; 

/************************************************
功能:输出给定入口地址的学生成绩对象的信息 
参数:学生成绩对象指针 TScore* pS
返回:无 
************************************************/
void F_Print(TScore* pS);

/************************************************
功能:计算给定学生成绩对象(内存中)的总分和平均分 
参数:学生成绩对象指针 TScore* pS 
返回:无 
************************************************/
void F_SumAndAverage(TScore* pS);

void F_SumAndAverage(TScore* pS){
	(*pS).Grade[3] = (*pS).Grade[0] + (*pS).Grade[1] + (*pS).Grade[2];//总分 
	(*pS).Grade[4] = (*pS).Grade[3] / 3.0;  //平均分 
}

void F_Print(TScore* pS){
	// 输出学生成绩对象S的属性
	printf("学生成绩:\n"); 
	printf("学号:%d\n", (*pS).Num);
	printf("语文:%.1f\n", (*pS).Grade[0]);
	printf("数学:%.1f\n", (*pS).Grade[1]);
	printf("英语:%.1f\n", (*pS).Grade[2]);
	printf("总分:%.1f\n", (*pS).Grade[3]);
	printf("平均:%.1f\n", (*pS).Grade[4]);
}

int main(int argc, char *argv[]){
	TScore S; //定义一个学生成绩对象(变量)
	
	//给对象的属性赋值 
	S.Num = 201601;    //学生成绩对象.学号 
	S.Grade[0] = 90.0; //学生成绩对象.语文成绩 
	S.Grade[1] = 87.5; //学生成绩对象.数学成绩 
	S.Grade[2] = 98.0; //学生成绩对象.英语成绩 
	
	F_SumAndAverage(&S); //参数是对象内存块的入口地址 

	F_Print(&S); //输出S的信息(给对象内存块的入口地址) 
	
	return 0;
}

2.3一个学生3门课程成绩:对象.属性 的等价简洁写法 指向对象的指针->属性
#include <stdio.h>

//定义学生成绩结构体类型
struct ST_score{
    int Num;        //学号(属性或字段) 
    float Grade[5]; //5个成绩(属性或字段)
};
//给自定义的数据类型 struct ST_score 定义一个别名
typedef struct ST_score TScore; 

/************************************************
功能:输出给定入口地址的学生成绩对象的信息 
参数:学生成绩对象指针 TScore* pS
返回:无 
************************************************/
void F_Print(TScore* pS);

/************************************************
功能:计算给定学生成绩对象(内存中)的总分和平均分 
参数:学生成绩对象指针 TScore* pS 
返回:无 
************************************************/
void F_SumAndAverage(TScore* pS);

void F_SumAndAverage(TScore* pS){
	pS->Grade[3] = pS->Grade[0] + pS->Grade[1] + pS->Grade[2];//总分 
	pS->Grade[4] = pS->Grade[3] / 3.0;  //平均分 
}

void F_Print(TScore* pS){
	// 输出学生成绩对象S的属性
	printf("学生成绩:\n"); 
	printf("学号:%d\n", pS->Num);
	printf("语文:%.1f\n", pS->Grade[0]);
	printf("数学:%.1f\n", pS->Grade[1]);
	printf("英语:%.1f\n", pS->Grade[2]);
	printf("总分:%.1f\n", pS->Grade[3]);
	printf("平均:%.1f\n", pS->Grade[4]);
}

int main(int argc, char *argv[]){
	TScore S; //定义一个学生成绩对象(变量)
	
	//给对象的属性赋值 
	S.Num = 201601;    //学生成绩对象.学号 
	S.Grade[0] = 90.0; //学生成绩对象.语文成绩 
	S.Grade[1] = 87.5; //学生成绩对象.数学成绩 
	S.Grade[2] = 98.0; //学生成绩对象.英语成绩 
	
	F_SumAndAverage(&S); //参数是对象内存块的入口地址 

	F_Print(&S); //输出S的信息(给对象内存块的入口地址) 
	
	return 0;
}

2.4N个学生3门课程成绩:N个学生成绩对象的集合(对象数组)
#include <stdio.h>

#define STD_NUM 5 //学生人数 

//定义学生成绩结构体类型
struct ST_score{
    int Num;        //学号(属性或字段) 
    float Grade[5]; //5个成绩(属性或字段)
};
typedef struct ST_score TScore; //别名 

/************************************************
功能:输入学生成绩数据 
参数:学生成绩对象数组的入口地址 TScore* pS
返回:无 
************************************************/
void F_InputData(TScore* pS);
void F_InputData(TScore* pS){
	int k;

	printf("输入学号和三门课程的成绩,样例如下:\n");
	printf("201601 100.0 99.0 88.0回车\n");
	for(k = 0; k < STD_NUM; k++){
		scanf("%d", &(pS[k].Num) );
		scanf("%f", &(pS[k].Grade[0]) ); 
		scanf("%f", &(pS[k].Grade[1]) ); 
		scanf("%f", &(pS[k].Grade[2]) ); 
	}	
}

/************************************************
功能:计算所有学生成绩对象(内存中)的总分和平均分 
参数:学生成绩对象数组的入口地址 TScore* pS 
返回:无 
************************************************/
void F_SumAndAverage(TScore* pS);
void F_SumAndAverage(TScore* pS){
	int k;
	
	for(k = 0; k < STD_NUM; k++) {
		pS[k].Grade[3] = pS[k].Grade[0] + \
			pS[k].Grade[1] + pS[k].Grade[2];   //总分 
		pS[k].Grade[4] = pS[k].Grade[3] / 3.0; //平均分 
	}
}

/************************************************
功能:打印所有学生的成绩报表 
参数:学生成绩对象数组的入口地址 TScore* pS
返回:无 
************************************************/
void F_PrintReport(TScore* pS);
void F_PrintReport(TScore* pS){
	int k, j;
	float Total[5] = {0.0}; //各列总分 
	 
	//报表表头
	printf("             学生成绩报表\n");
	printf("---------------------------------------\n"); 
	printf("%-6s ", " 学号");
	printf("%-5s ", " 语文");
	printf("%-5s ", " 数学");
	printf("%-5s ", " 英语");
	printf("%7s ", "总分");
	printf("%5s\n", "平均");
	printf("---------------------------------------\n"); 
	
	//内容和统计 
	for(k = 0; k < STD_NUM; k++) {
		printf("%6d ", pS[k].Num);
		printf("%5.1f ", pS[k].Grade[0]);
		printf("%5.1f ", pS[k].Grade[1]);
		printf("%5.1f ", pS[k].Grade[2]);
		printf("%7.1f ", pS[k].Grade[3]);
		printf("%5.1f\n", pS[k].Grade[4]);
		
		//各项总分 
		for(j = 0; j < 5; j++){
			Total[j] += pS[k].Grade[j];	
		} 
	}
	
	//表尾部 
	printf("---------------------------------------\n"); 
	printf("%-6s ", "平均");
	printf("%5.1f ", Total[0]/STD_NUM);
	printf("%5.1f ", Total[1]/STD_NUM);
	printf("%5.1f ", Total[2]/STD_NUM);
	printf("%7.1f ", Total[3]/STD_NUM);
	printf("%5.1f ", Total[4]/STD_NUM);
	printf("\n");
}

int main(int argc, char *argv[]){
	TScore S[STD_NUM]; //定义一个学生成绩对象(变量)
	
	F_InputData(S); //输入数据(参数是对象数组的入口地址) 
	
	F_SumAndAverage(S); //计算总分平均分(参数是对象数组的入口地址) 

	F_PrintReport(S); //输出报表(参数是对象数组的入口地址) 
	
	return 0;
}

在这里插入图片描述

2.5课程信息管理

//定义课程结构体类型(课程的结构)

struct S_Course {

int Code; //课程代号 

​ char Name[21]; //课程名称

​ char TypeID; //课程性质代号(0-4)

​ int Hours; //学时

​ float Credit; //学分

};

//给自定义的数据类型 struct S_Course定义一个别名

typedef struct S_Course TCourse;

//程序中使用自定义的数据类型

TCourse Cs[50]; //50门课程的集合(数组)

Cs[0].Code = 1; //第0门课程的代号

strcpy(Cs[0].Name, “C程序设计”); //第0门课程的名称

#include <stdio.h>

定义符号常量 
#define COURSE_NUM 5 //课程门数 
#define TSJY 0 //通识教育 
#define SZJY 1 //素质教育 
#define ZYJC 2 //专业基础 
#define ZYFX 3 //专业方向 
#define ZYXX 4 //专业选修 
#define SSKC 127 //所有课程 

课程性质的字符串数组(5行9列)
char CourseTypeStr[5][9] ={
	"通识教育", //第0行 
	"素质教育", //第1行
	"专业基础", //第2行 
	"专业方向", //第3行 
	"专业选修"  //第4行 
};

课程结构体类型(课程对象类型) 
struct ST_Course{
	int Code;       //课程代号
	char Name[21];  //课程名称
	char TypeID;    //课程类型代号(0-4) 
	int Hours;      //学时
	float Credit;   //学分
};
typedef struct ST_Course TCourse; //类型别名 

/************************************************
功能:输入课程数据 
参数:课程数组的入口地址 TCourse* pC 
返回:无 
************************************************/
void F_InputData(TCourse* pC);
void F_InputData(TCourse* pC){
	int k;

	printf("输入课程编号、名称、性质代号、学时、学分\n");
	printf("输入样例:1 计算机基础 2 32 2.0回车\n");
	for(k = 0; k < COURSE_NUM; k++){
		scanf("%d", &(pC[k].Code) );
		scanf("%s", &(pC[k].Name) ); 
		scanf("%d", &(pC[k].TypeID) ); 
		scanf("%d", &(pC[k].Hours) ); 
		scanf("%f", &(pC[k].Credit) ); 
	}	
}

/************************************************
功能:打印某类课程的报表 
参数:课程数组的入口地址 TCourse* pC, 课程性质代号 
返回:无
************************************************/
void F_PrintReport(TCourse* pC, char CourseTypeID);
void F_PrintReport(TCourse* pC, char CourseTypeID){
	int k, j;
	 
	//报表表头
	if(CourseTypeID == SSKC) printf("        课程信息报表(全部课程)\n");
	else printf("        课程信息报表(%s)\n", CourseTypeStr[CourseTypeID]);
	printf("---------------------------------------------\n"); 
	printf("%-4s ", "代号");
	printf("%-20s ", "名称");
	printf("%-8s ", "性质");
	printf("%-4s ", "学时");
	printf("%-4s\n", "学分");
	printf("---------------------------------------------\n"); 
	
	//内容
	for(k = 0; k < COURSE_NUM; k++) {
		//参数是所有课程或者课程的性质等于参数指定的课程类型,输出 
		if(CourseTypeID == SSKC || pC[k].TypeID == CourseTypeID){
			printf("%4d ", pC[k].Code);
			printf("%-20s ", pC[k].Name);
			printf("%-8s ", CourseTypeStr[pC[k].TypeID]);
			printf("%4d ", pC[k].Hours);
			printf("%4.1f\n", pC[k].Credit);
		}
	}
	
	//表尾部 
	printf("---------------------------------------------\n"); 
}

int main(int argc, char *argv[]){
	TCourse Cs[COURSE_NUM];
	
	F_InputData(Cs); //输入数据(参数是对象数组的入口地址) 
	
	F_PrintReport(Cs, SSKC); //输出所有课程 
	F_PrintReport(Cs, TSJY); //输出通识教育课程 
	F_PrintReport(Cs, SZJY); //输出素质教育课程 
	F_PrintReport(Cs, ZYJC); //输出专业基础课程 
	F_PrintReport(Cs, ZYXX); //输出专业基础课程 

	return 0;
}

3.自定义枚举数据类型

在这里插入图片描述

#include <stdio.h>

enum E_Color{
	RED,  	//0 
	ORANGE, //1 
	YELLOW, //2 
	GREEN,  //3 
	BLUE,   //4 
	INDIGO, //5 
	VIOLET  //6 
};
typedef enum E_Color EColor;

char ColorStr[7][7] = {
	"RED",
	"ORANGE",
	"YELLOW",
	"GREEN",
	"BLUE",
	"INDIGO",
	"VIOLET"
};

int main(int argc, char *argv[])
{
	EColor ColorX; //ColorX是一个非负整数 
	
	for(ColorX = RED; ColorX <= VIOLET; ColorX++)
	{
		printf("%d:%s\n", ColorX, ColorStr[ColorX]);
	}

	return 0;
}

#include <stdio.h>

enum E_Weekday{
	SUNDAY, 	//0 
	MONDAY, 	//1 
	TUESDAY, 	//2 
	WENDNESDAY,	//3 
	THURSDAY,	//4 
	FRIDAY, 	//5 
	SATURDAY  	//6 
};
typedef enum E_Weekday EWeekday;

char WeekDayStr[7][2][11] = {
	"SUNDAY", 		"周日", 
	"MONDAY", 		"周一", 
	"TUESDAY", 		"周二", 
	"WENDNESDAY", 	"周三", 
	"THURSDAY", 	"周四", 
	"FRIDAY", 		"周五", 
	"SATURDAY", 	"周六"
};

int main(int argc, char *argv[]){
	EWeekday WeekdayX; //WeekdayX是一个非负整数 
	
	for(WeekdayX = SUNDAY; WeekdayX <= SATURDAY; WeekdayX++){
		printf("%d:", WeekdayX);
		printf("%s\t", WeekDayStr[WeekdayX][0]); //英文 
		printf("%s\n", WeekDayStr[WeekdayX][1]); //中文 
	}

	return 0;
}

4.数据类型

数据类型(Data Type):把具有相同特征 的数据归为一类,给一个名称,就是一种 数据类型,或者说,数据类型是具有相同 特性的数据集的抽象。这样同类数据就可以用相同的方式进行处理。

#include <stdio.h>

//颜色枚举类型 
enum E_Color{
	RED,  	//0 
	ORANGE, //1 
	YELLOW, //2 
	GREEN,  //3 
	BLUE,   //4 
	INDIGO, //5 
	VIOLET  //6 
};
typedef enum E_Color EColor;

//颜色枚举符号对应的字符串 
char ColorStr[7][7] = {
	"红色",
	"橙色",
	"黄色",
	"绿色",
	"蓝色",
	"靛色",
	"紫色"
};

//形状枚举类型 
enum E_Shape{
	CIRCLE, 	//0-圆形 
	SQUARE, 	//1-正方形 
	RECTANGLE	//2-长方形 
};
typedef enum E_Shape EShape;

//形状枚举符号对应的字符串 
char ShapeStr[3][7] = {
	"圆形",
	"正方形",
	"长方形"
};

桌面结构体类型 
struct ST_DeskTop{
	EColor Color; //颜色 
	EShape Shape; //形状 
	int Weight;	  //重量(kg) 
};
typedef struct ST_DeskTop TDeskTop;

桌腿结构体类型 
struct ST_DeskLeg{
	EColor Color; //颜色 
	int Weight;	  //重量(kg) 
	int Height;   //高度(cm) 
};
typedef struct ST_DeskLeg TDeskLeg;

struct ST_TriDesk{
	TDeskTop DeskTop; //桌面 
	TDeskLeg DeskLegA; //桌腿A 
	TDeskLeg DeskLegB; //桌腿B
	TDeskLeg DeskLegC; //桌腿C 
};
typedef struct ST_TriDesk TTriDesk;

int main(int argc, char *argv[])
{
	TDeskTop DeskTopX; //桌面 
	TDeskLeg DeskLeg1; //桌腿1
	TDeskLeg DeskLeg2; //桌腿2
	TDeskLeg DeskLeg3; //桌腿3 
	TTriDesk TriDesk;  //三脚桌子 
	
	//桌面
	DeskTopX.Color = YELLOW;
	DeskTopX.Shape = CIRCLE;
	DeskTopX.Weight = 5;
	//桌腿1
	DeskLeg1.Color = RED;
	DeskLeg1.Weight = 2;
	DeskLeg1.Height = 100;
	//桌腿2 
	DeskLeg2 = DeskLeg1;
	DeskLeg2.Color = BLUE;
	//桌腿3
	DeskLeg3 = DeskLeg1;
	DeskLeg3.Color = GREEN;
	//桌面
	TriDesk.DeskTop = DeskTopX;
	TriDesk.DeskLegA = DeskLeg1;
	TriDesk.DeskLegB = DeskLeg2;
	TriDesk.DeskLegC = DeskLeg3;
	
	printf("三脚桌的信息:\n");
	printf(" 桌面:"); 
	printf("%s ", ColorStr[TriDesk.DeskTop.Color]);
	printf("%s ", ShapeStr[TriDesk.DeskTop.Shape]);
	printf("%dkg\n", TriDesk.DeskTop.Weight);
	printf("桌腿A:"); 
	printf("%s ", ColorStr[TriDesk.DeskLegA.Color]);
	printf("%dkg ", TriDesk.DeskLegA.Weight);
	printf("%dcm\n", TriDesk.DeskLegA.Height);
	printf("桌腿B:"); 
	printf("%s ", ColorStr[TriDesk.DeskLegB.Color]);
	printf("%dkg ", TriDesk.DeskLegB.Weight);
	printf("%dcm\n", TriDesk.DeskLegB.Height);
	printf("桌腿C:"); 
	printf("%s ", ColorStr[TriDesk.DeskLegC.Color]);
	printf("%dkg ", TriDesk.DeskLegC.Weight);
	printf("%dcm\n", TriDesk.DeskLegC.Height);

	return 0;
}

第八章 文件IO

1.概述

1.1文件

• 操作系统把每一个与主机相联的输入输出设备都看作是文件, 输入文件:键盘、磁盘等 ,输出文件:显示设备、打印机、磁盘等

• 数据文件:存储在外存上数据的集合 数据以文件的形式存放在外存中,操作 系统以文件为单位来管理外存数据 读取外存上的数据:先按文件名找到文件,然后再从该文件读数据 存储数据到外存上:必须先建立一个文 件,然后再输出(写)数据到外部介质上

1.2数据持久化

• 程序运行时,程序处理的数据都存储在内存中,程序终止后,内存中的数据无法再使用。要想重复使用数据,只有在 程序运行时把内存中的数据写到磁盘等 外存文件中,下一次运行程序时从外存 文件中读取数据到内存中

• 数据持久化:把内存中的数据存储在断电后不会改变状态的外存中

• 对机器来说,一切皆数据!文本文件、 Word文件、可执行文件、源代码文件、 数据文件……

1.3文件中的数据存储方式

按照数据的存储形式,文件分为两种:

◼ 二进制文件 内存中的任何数据本质上都是以二进制 形式存储的,如果把内存的数据逐字节 或成块地输出存储到外存文件中,该文 件就是二进制文件

◼ ASCII文件 又称文本文件,文件中按规定的格式存 储数据字符的ASCII编码值。读写时需要 按照约定做转换:内存中的二进制数据 - 约定格式的数据字符(文本文件中)

1.4读写/存取 文件的过程

• 由操作系统(OS)管控的程序在读写 文件时,需要向OS发出读写请求, OS的“文件系统”专门用来处理程 序的文件读写请求及相关操作

• 程序读写文件时,OS的文件系统会 自动地在内存区为程序中每一个正 在使用的文件开辟一个文件缓冲区

• 写:程序数据先送到文件缓冲区, 缓冲区满或数据读完一次写到磁盘

• 读:一次从磁盘文件将一批数据输 入到内存缓冲区,然后再从缓冲区 逐个地将数据送到程序数据区(给 程序变量)

• I/O是机器的不同设备间传送数据的过程

• 数据如流水一样从一处流向另一处,因 此I/O也被称为流(stream),即数据流

• 输入操作(读)时,数据从文件流向内存

• 输出操作(写)时,数据从内存流向文件

1.5 C程序如何读写文件?

• 每个被读写的文件都有一个专 用的文件缓冲区(内存)来存 放该文件的基本信息和数据

• C标准函数库(stdio.h中)提供 了表达文件缓冲区(对象)的 结构体类型FILE,其属性包括 文件名、文件状态、大小、缓 冲数据的位置等

• C标准函数库提供了一系列非 常易于理解的读写文件相关的 函数,每个函数都要使用文件 指针(参数)来读写文件

读写过程

S1:定义文件指针(指向文件缓冲区)

S2:打开文件fopen():建立文件缓冲区, 标记文件被占用,获得缓冲区地址

S3:读写文件(访问文件缓冲区)

S4:关闭文件fclose():关闭文件缓冲区, 释放文件(标记文件未被占用)

在这里插入图片描述

2.写字符到文本文件(文件缓冲区或文件流),从文件流读字符

写字符到ASCII文件

int fputc(int ch, FILE* stream);

• 把字符ch写到输出流stream

• 成功返回字符, 错误返回EOF(-1)

#include <stdio.h>

int main(int argc, char *argv[]){
	FILE* fp;  //定义文件指针
	int k;
	
	fp = fopen("C0901.txt", "w"); //写文本文件 
	
	//写文件
	if(fp == NULL) printf("新建C0901.txt文件失败!"); 
	else{ 
		for(k = 0; k < 128; k++ ){
			//写字符k到fp指向的文件缓冲区
			fputc(k, fp);	
		}
	}

	fclose(fp);//关闭文件,释放文件缓冲区内存块 
	
	return 0;
}

从ASCII文件读字符

int fgetc(FILE *fp);

返回来自fp文件流中 的下一个字符,如果 到达文件尾或者发生 错误时返回EOF(-1)

int feof(FILE* pf);

探测pf指向的文件流是否已经到达文件尾, 是返回一个非零值,否返回0

#include <stdio.h>

int main(int argc, char *argv[]){
	FILE* fp;  //定义文件指针
	int k;
	
	fp = fopen("C0901.txt", "r"); //读文本文件
	
	//读文件中的数据 
	if(fp == NULL) printf("打开C0901.txt文件失败!"); 
	else{ 
		while(1){
			k = fgetc(fp); //从流fp中读取一个字符 
			if(feof(fp)) break; //文件结束则退出循环 
			
			putchar(k); //输出字符k到屏幕上 
		} 
	}

	fclose(fp);//关闭文件 
	
	return 0;
}

显示文本文件的内容

#include <stdio.h>

int main(int argc, char *argv[]){
	FILE* fp;  //定义文件指针
	int k;
	
	fp = fopen("C0903.c", "r"); //读文本文件
	
	//读文件中的数据 
	if(fp == NULL) printf("打开C0903.c文件失败!"); 
	else{ 
		while(1){
			k = fgetc(fp); //从流fp中读取一个字符 
			if(feof(fp)) break; //文件结束则退出循环 
			
			putchar(k); //输出字符k到屏幕上 
		} 
	}

	fclose(fp);//关闭文件 
	
	return 0;
}

用fgetc()和fputc()函数复制文件:读源文件,写目标文件

#include <stdio.h>

int main(int argc, char *argv[]){
	FILE* pfFrom;  //源文件指针
	FILE* pfTo;  //目标文件指针
	int k;
	
	pfFrom = fopen("C0904.c", "r"); //读源文件
	pfTo = fopen("C0904副本.c", "w"); //写目标文件
	
	//读文件中的数据 
	if(pfFrom == NULL) printf("打开 C0904.c 文件失败!"); 
	else if(pfTo == NULL) printf("创建 C0904副本.c 文件失败!"); 
	else{ 
		while(1){
			k = fgetc(pfFrom); //从源文件流读取一个字符 
			if(feof(pfFrom)) break; //文件结束则退出循环 

			fputc(k, pfTo); //写字符到目标文件流			
		} 
		
		printf("文件复制成功!\n"); 
	}

	fclose(pfFrom);//关闭源文件 
	fclose(pfTo);//关闭目标文件 
	
	return 0;
}

3.写字符串到输出文件流,从输入文件流读字符串

调用fputs()函数写字符串到输出文件流

int fputs(const char *str, FILE *pfw);

• 把字符串str写到pfw文件流中

• 成功返回非负值, 失败返回EOF(-1)

#include <stdio.h>

int main(int argc, char *argv[]){
	FILE* pfw;
	
	pfw = fopen("C0905.txt", "w"); //写文本文件
	
	//写字符串到输出文件流 
	if(pfw == NULL) printf("创建 C0905.txt 文件失败!"); 
	else{
		fputs("All we all is dust in the wind!\n", pfw);	
		fputs("Everything is dust in the wind!\n", pfw);	
	} 

	fclose(pfw);//关闭文件 	

	return 0;
}

调用fgets()函数从输入文件流读字符串

char* fgets(char* str, int n, FILE* pfr);

• 从pfr文件流中读取 n-1 个字符,并把 它们存储到str字符串(字符数组)中

• 若读了 n-1 个字符还未遇到’\n’,则在 str尾部加上’\n’(str[n-1] = ‘\n’)

• 若已经读了 n-1 个字符 或 读到文件结 束符EOF,则str[n-1] = NULL(‘\0’)

• 函数成功返回str字符串的入口地址 失败返回NULL

#include <stdio.h>
#define STR_LENGTH 5

int main(int argc, char *argv[]){
	FILE* pfr;
	char str[STR_LENGTH];
	
	pfr = fopen("C0905.txt", "r"); //写文本文件
	
	//读文件
	if(pfr == NULL) printf("打开 C0905.txt 文件失败!"); 
	else{
		while(1){
			fgets(str, STR_LENGTH, pfr);
			if( feof(pfr) ) break;
			
			printf("%s", str);
		} 
	} 

	fclose(pfr);//关闭文件 	

	return 0;
}

用fgets()和fputs()复制文件:读源文件,写目标文件

#include <stdio.h>
#define STR_LENGTH 50

int main(int argc, char *argv[]){
	FILE* pfFrom;  //源文件指针
	FILE* pfTo;  //目标文件指针
	char str[STR_LENGTH];
	
	pfFrom = fopen("C0907.c", "r"); //读源文件
	pfTo = fopen("C0907副本.c", "w"); //写目标文件
	
	//读文件中的数据 
	if(pfFrom == NULL) printf("打开 C0907.c 文件失败!"); 
	else if(pfTo == NULL) printf("创建 C0907副本.c 文件失败!"); 
	else{ 
		while(1){
			fgets(str, STR_LENGTH, pfFrom);//读源文件流的字符串 
			if( feof(pfFrom) ) break;

			fputs(str, pfTo); //写字符串到目标文件流			
		} 
		
		printf("文件复制成功!\n"); 
	}

	fclose(pfFrom);//关闭源文件 
	fclose(pfTo);//关闭目标文件 
	
	return 0;
}

4.按指定的格式读写文本文件:fprintf()和fscanf()函数

int fprintf( FILE* fpw, const char* format, … );

• 以printf()函数的执行方式把字符串 format写到fpw输出文件流中

• 成功返回输出的字符数,失败返回负数

#include <stdio.h>
int main(int argc, char *argv[]){
	FILE* pfw;
	float S[5][3] = {
		99, 88, 77,
		98, 87, 76,
		97, 86, 75,
		96, 85, 74,
		91, 83, 72
	};
	int k;
	
	pfw = fopen("C0908.txt", "w");//写文本文件
	
	if(pfw == NULL) printf("新建C0908.txt文件失败!");  
	else{
		for(k = 0; k < 5; k++){
			fprintf(pfw, "%f,%f,%f\n", S[k][0], S[k][1], S[k][2]);
		}
	} 
	
	fclose(pfw);	//关闭文件 
	
	return 0;
}

int fscanf( FILE *pfr, const char *format, … );

• 以scanf()的执行方式从pfr文件流中读 取数据(写到指定的内存位置处)

• 成功返回已赋值的变量数,失败返回EOF

#include <stdio.h>
int main(int argc, char *argv[]){
	FILE* pfr;
	float S[5][3] = {0};
	int k;
	
	pfr = fopen("C0908.txt", "r");//读文本文件
	
	if(pfr == NULL) printf("打开C0908.txt文件失败!");  
	else{
		for(k = 0; k < 5; k++){
			fscanf(pfr, "%f,%f,%f\n", &S[k][0], &S[k][1], &S[k][2]);
			printf("%f %f %f\n", S[k][0], S[k][1], S[k][2]);
		}
	} 
	
	fclose(pfr);	//关闭文件 
	
	return 0;
}

5.读写二进制文件:fwrite()和fread()函数

将内存数据写到二进制文件流中

int fwrite(const void* buffer, size_t size, size_t count, FILE* stream );

• 从buffer指向的内存位置开始, 将count* size个字节的内存数 据写到stream文件数据流尾部

• 返回成功写出的内存块数

• typedef unsigned int size_t

#include <stdio.h>
int main(int argc, char *argv[]){
	FILE* pfwb;
	float S[5][3] = {
		99, 88, 77,
		98, 87, 76,
		97, 86, 75,
		96, 85, 74,
		91, 83, 72
	};
	
	pfwb = fopen("C0910.PAN", "wb");//写二进制文件
	
	if(pfwb == NULL) printf("新建C0910.PAN文件失败!");  
	else{
		//写15个内存块(元素)数据到pfwb二进制文件流
		//每块大小是一个元素占用内存的字节数 
		fwrite(S, sizeof(float), 5*3, pfwb); 
	} 
	
	fclose(pfwb);	//关闭文件 
	
	return 0;
}

用fread()函数从二进制文件读取数据并写到指定内存位置

int fread(void* buffer, size_t size, size_t count, FILE* stream );

• 从stream文件数据流读取 count*size个字节的数据,写 到buffer地址开始的内存中

• 返回成功读取的内存块数

#include <stdio.h>
int main(int argc, char *argv[]){
	FILE* pfrb;
	float S[5][3] = {0};
	int k;
	
	pfrb = fopen("C0910.PAN", "rb");//读二进制文件
	
	if(pfrb == NULL) printf("文件C0910.PAN不存在!");  
	else{
		//从pfrb文件流读取sizeof(float)*5*3个字节的数据
		//并且把数据写到S地址开始的内存块中 
		fread(S, sizeof(float), 5*3, pfrb); 
		
		for(k = 0; k < 5; k++){
			printf("%f %f %f\n", S[k][0], S[k][1], S[k][2]);
		}
	} 
	
	fclose(pfrb);	//关闭文件 
	
	return 0;
}

用fread()函数从二进制文件读取数据并写到指定内存位置:不知道数据个数

#include <stdio.h>
int main(int argc, char *argv[]){
	FILE* pfrb; //定义文件指针(指向文件信息区)
	float a; 
	int IsOK;  //读取到数据否 
	int Count; //读取的数据个数 
	
	pfrb = fopen("C0910.PAN", "rb"); //打开文件(创建文件信息区)
	
	//读写文件
	if(pfrb == NULL) printf("文件C0910.PAN不存在!\n");
	else{ //不知道文件里存的数据个数,只能构造死循环来读取 
		Count = 0; 
		while(1){
			//从文件缓冲区中读一个int类型数据写到变量a的内存中
			//fread函数读取失败(文件结束)返回0 
			IsOK = fread(&a, sizeof(float), 1, pfrb);	
			if(IsOK == 0) break;
			
			Count++; //读取到的数据个数 
			printf("%4d:%f\n", Count, a); 
		}
	}

	fclose(pfrb); //关闭文件缓冲区 
	
	return 0;
}

6.写课程信息到二进制文件,从二进制文件读取课程信息

//功能:写课程信息到课程数据二进制文件 
#include <stdio.h>

struct ST_Course{
	int Code; //课程代号 
	char Name[20];//课程名称 
	int Credit;//学分 
	int Hours; //学时 
};
typedef struct ST_Course TCourse;

int main(int argc, char *argv[]){
	TCourse Cs[3] = {
		1,	"计算机基础",	2,	32,
		2,	"高等数学",		4,	72,
		3,	"线性代数",		3, 	54
	};
	FILE* pfwb;
	
	pfwb = fopen("课程信息.txt", "wb"); //写二进制文件 
	
	if(pfwb == NULL) printf("新建 课程信息.txt 文件失败!");  
	else{
		//把Cs数组的所有内存数据写入二进制文件 
		fwrite(Cs, sizeof(TCourse), 3, pfwb); 
	} 
	
	fclose(pfwb);//关闭文件 
	
	return 0;
}
//从课程信息.txt二进制文件读取课程信息 
#include <stdio.h>

struct ST_Course{
	int Code; //课程代号 
	char Name[20];//课程名称 
	int Credit;//学分 
	int Hours; //学时 
};
typedef struct ST_Course TCourse;

int main(int argc, char *argv[]){
	TCourse Cs[3];
	FILE* pfrb;
	int k; 
	
	pfrb = fopen("课程信息.txt", "rb"); //读二进制文件 
	
	if(pfrb == NULL) printf("打开 课程信息.txt 文件失败!");  
	else{
		//把pfrb文件流的数据读入Cs数组的内存中 
		fread(Cs, sizeof(TCourse), 3, pfrb); 
		
		for(k = 0; k < 3; k++){
			printf("%4d ", Cs[k].Code);
			printf("%-20s ", Cs[k].Name);
			printf("%2d ", Cs[k].Credit);
			printf("%3d\n", Cs[k].Hours);
		}
	} 
	
	fclose(pfrb);//关闭文件 
	return 0;
}

第九章 动态内存

1.提出问题

• C 程序的常量、变量和数组在编译时确 定了要占用的内存大小(多少字节)

• 程序运行时(初始化阶段),操作系统 为程序分配必需的内存空间,常量、变 量和数组占用内存块的入口地址和内存 块的大小是固定不变的

• 多数情况下,学生、课程等集合的元素 个数(长度)不是固定的,怎么办?

• 设计程序时给数组足够大的长度?程序 运行时,实际的元素个数少则浪费内存 空间,多则不够存放。"足够"太模糊!

解决方案:程序运行过程中,集合中新增一 个元素则申请相应的内存,删除一个元素则 释放相应的内存。有多少用多少

• 数组占用的内存是连续的,如果数组需 要的内存太大,而系统中没有足够大的 连续内存可用,那么程序将无法工作

• 内存中可能有很多零散的小内存块可以 使用,如果集合的每个元素独自占用一 个内存块,则可以充分利用零散的内存 块。如何找到遍历集合的每个元素?

在这里插入图片描述

2.动态申请内存的函数

调用calloc()函数申请内存块

#include <stdio.h>
#include <stdlib.h>

#define NUM 5 //5个元素 

int main(int argc, char *argv[]){
	void* pEntry = NULL; //存放动态申请内存块的入口地址 
	int* pInt = NULL; //以int类型的数据为一个单位读写内存 
	int k;
	
	//申请 NUM * sizeof(int) 字节 的内存块 
	pEntry = calloc(NUM, sizeof(int));
	if(pEntry == NULL){
		printf("内存申请失败!\n");
		return 0; //后面的代码不再执行,程序结束 
	}
	
	/申请到内存了
	//写数据到内存块
	pInt = pEntry; //把入口地址赋值给指向int单元的指针 
	for(k = 0; k < NUM; k++){
		pInt[k] = k + 80; //把k写到第k个整数单元中 
	}
	
	//输出数据到显示器
	for(k = 0; k < NUM; k++){
		printf("%d\n", pInt[k]);
	}
	
	free(pEntry); //释放申请的内存 
	
	return 0;
}

#include <stdio.h>
#include <stdlib.h>

#define NUM 5

int main(int argc, char *argv[]){
	void* pEntry = NULL;//存放内存块的入口地址 
	int* pInt = NULL;
	int k;
	char* pChar;
	
	//申请内存 NUM * sizeof(int) 字节数 
	pEntry = calloc(NUM, sizeof(int));
	if(pEntry == NULL) 	{
		printf("内存申请失败!\n");
		return 0;
	}
	
	/申请到内存了
	//从键盘输入NUM个整数,写数据到内存块
	pInt = pEntry; 
	printf("输入%d个整数:\n", NUM);
	for(k = 0; k < NUM; k++)	{
		scanf("%d", pInt+k);//第k个单元的入口地址  
	}
	
	//输出数据到显示器
	for(k = 0; k < NUM; k++)	{
		printf("%d\n", *(pInt+k) );
	}

	free(pEntry); //释放申请的内存 
	
	return 0;
}

调用malloc()函数申请内存块

#include <stdio.h>
#include <stdlib.h>

#define NUM 5 //5个元素 

int main(int argc, char *argv[]){
	void* pEntry = NULL; //存放动态申请内存块的入口地址 
	int* pInt = NULL; //以int类型的数据为一个单位读写内存 
	int k;
	
	//申请 NUM * sizeof(int) 字节 的内存块 
	pEntry = malloc(NUM * sizeof(int));
	if(pEntry == NULL){
		printf("内存申请失败!\n");
		return 0; //后面的代码不再执行,程序结束 
	}
	
	/申请到内存了
	//写数据到内存块
	pInt = pEntry; //把入口地址赋值给指向int单元的指针 
	for(k = 0; k < NUM; k++){
		pInt[k] = k + 80; //把k写到第k个整数单元中 
	}
	
	//输出数据到显示器
	for(k = 0; k < NUM; k++){
		printf("%d\n", pInt[k]);
	}
	
	free(pEntry); //释放申请的内存 
	
	return 0;
}

#include <stdio.h>
#include <stdlib.h>

#define NUM 5 //5个元素 

int main(int argc, char *argv[]){
	void* pEntry = NULL; //存放动态申请内存块的入口地址 
	int* pInt = NULL; //以int类型的数据为一个单位读写内存 
	char* pChar = NULL; //以1字节的字符为一个单位读写内存 
	int k;
	
	//申请 NUM * sizeof(int) 字节 的内存块 
	pEntry = malloc(NUM * sizeof(int));
	if(pEntry == NULL) 	{
		printf("内存申请失败!\n");
		return 0;
	}
	
	/申请到内存了
	//从键盘输入数据并写到内存块
	pInt = pEntry; //内存块入口地址 
	printf("输入%d个整数:\n", NUM);
	for(k = 0; k < NUM; k++)	{
		scanf("%d", pInt+k);//第k个单元的入口地址  
	}
	
	//输出整数到显示器 
	for(k = 0; k < NUM; k++)	{
		printf("%d ", pInt[k]);
	}
	printf("\n");
	
	//输出字符到显示器 
	pChar = pEntry; //内存块入口地址 
	for(k = 0; k < NUM * sizeof(int); k++)	{
		printf("%c", pChar[k]);
	}
	printf("\n");
	
	free(pEntry); //释放申请的内存 
	
	return 0;
}

内存中的数据 是什么完全由 读写该内存的 数据类型决定

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
	int* pInt = NULL; //存放动态申请内存块的入口地址 
	int Count; //int类型的整数总个数 
	int Number; 
	
	//先申请一个整数单元的内存块
	//函数返回的指针类型是void*,需要强制转换为int* 
	pInt = malloc( sizeof(int) );
	
	//不断输入,直到用户输入-1为止 
	Count = 0; 
	while(1){
		printf("输入正整数(输入-1结束):");
		scanf("%d", &Number); 
		if(Number == -1) break;
		
		Count++;//计数器加1
		//调整内存大小 
		pInt = realloc(pInt, Count * sizeof(int));
		//把输入的数据写到 Count-1 号单元中 
		pInt[Count-1] = Number; //注意下标从0开始 
	} 
	
	//倒着输出整数到显示器 
	while( Count > 0 ){
		printf("%d\n", pInt[Count-1]);
		Count--;		
	}

	free(pInt); //释放申请的内存 
	
	return 0;
}

按需调整动态申 请的内存块大小 又称为动态数组 技术,内存块中 存放数据的方式 本质上和数组没 有区别,其中每 个数据可看作数 组的一个元素

需要强制性记忆:

①程序中任何类型的常量、 变量和数组等数据都占用 一个连续内存块,内存块 大小(字节数)由其类型决 定【用sizeof()获取】

②对于任何内存块,只要 知道其入口地址,就可以 用任意数据类型的指针来 访问其中的数据(内存块中 的数据是什么由读写时所 用的数据类型决定)

③用数组元素下标访问数 据等价于以入口地址(数组 名)为基准做移址和寻址操 作,a[5] 等价于 *(a+5)

3.单链表

单向链表:新增节点、删除节点、查找节点、按某种条件排序、释放链表

综合案例

#include <stdio.h>
#include <stdlib.h> 

//定义课程节点结构体
struct ST_CourseNode{
	int Code;//课程代号
	char Name[20];//课程名称
	int Credit;//学分
	int Hours;//学时
	struct ST_CourseNode* pNext; //课程节点指针	
};
typedef struct ST_CourseNode TCourseNode; 

//整个程序中都要用到链表的头节点指针和尾节点指针
//把这两个指针变量的定义放在所有函数之前
//这类变量成为“全局变量”,全局有效的变量(内存)
//全局变量在程序而不是函数的内存空间中 
TCourseNode* pHead = NULL; //指向链表首节点的指针,初始NULL 
TCourseNode* pTail = NULL; //指向链表尾节点的指针,初始NULL 

/************************************************
功能:新建课程节点,输入课程信息
参数:void
返回: 成功则返回新节点的入口地址,失败返回NULL 
************************************************/ 
TCourseNode* F_NewNode(void);
TCourseNode* F_NewNode(void){
	TCourseNode* p; //指向节点的指针 
	
	//申请分配存放一个节点的内存块 
	p = (TCourseNode*)malloc( sizeof(TCourseNode) );
	if( p == NULL ) printf("内存已满!");
	else { //分配成功 
		//输入课程信息 
		printf("请输入课程代号、名称、学分、学时:\n");		
		scanf("%d", &(p->Code) );
		scanf("%s", p->Name );
		scanf("%d", &(p->Credit) );
		scanf("%d", &(p->Hours) );
		p->pNext = NULL; //新节点没有后继节点 
	}
		
	return p; //返回NULL或新节点地址入口地址 
}

/************************************************
功能:把给定的新节点链接到链表尾部 
参数:给定的新节点的入口地址 
返回: void 
************************************************/ 
void F_LinkToTail(TCourseNode* pNew);
void F_LinkToTail(TCourseNode* pNew){
	//链接到链表中
	if( pHead == NULL ) {//链表空
		pHead = pNew;//新节点是首节点
		pTail = pNew;//新节点是尾节点 
	}
	else{ //链接到链表尾部
		 pTail->pNext = pNew; //新节点链接到当前尾节点后 
		 pTail = pNew; //新节点成为尾节点 
	} 
}

/************************************************
功能:输入课程信息,把输入的课程信息链接到链表尾部 
参数:void
返回: void 
************************************************/ 
void F_InputData(void);
void F_InputData(void){
	TCourseNode* p; //指向节点的指针 
	int Answer; 
	
	//动态新增节点,直到用户选择不再新增为止
	while(1){
		p = F_NewNode(); //创建新课程节点 
		if(p == NULL) break; //创建失败循环结束 
		
		F_LinkToTail(p);//把p指向的新节点链接到链表尾部 
		
		printf("继续输入课程信息?0结束,其它键继续 \n");	
		Answer = 0;	
		scanf("%d", &Answer );
		if(Answer == 0) break;
	} 
}

/************************************************
功能:输出链表中所有课程的信息(非报表样式) 
参数:void
返回: void 
************************************************/ 
void F_PrintAllCourse(void);
void F_PrintAllCourse(void){
	TCourseNode* p; //指向节点的指针 

	//遍历链表 (打印信息)
	p = pHead;//链表的首节点的地址给p 
	while(p != NULL){
		//打印p指向的课程节点的课程信息 
		printf("代号:%d\n", p->Code );
		printf("名称:%s\n", p->Name );
		printf("学时:%d\n", p->Credit );
		printf("学分:%d\n", p->Hours );
		
		//把p指向的节点的指针值(地址)赋值给p
		//让p指向后继节点(p == NULL表示没有后继节点了)
		p = p->pNext; 
	} 
}

/************************************************
功能:输出链表中所有课程的信息(报表样式) 
参数:void
返回: void 
************************************************/ 
void F_PrintReporting(void);
void F_PrintReporting(void){
	TCourseNode* p; //指向节点的指针 

	//报表头
	printf("              课程信息\n"); 
	printf("-----------------------------------\n"); 
	printf("%4s ", "代号");
	printf("%-20s ", "名称");
	printf("%4s ", "学时");
	printf("%4s\n", "学分");
	printf("-----------------------------------\n"); 

	//课程信息 
	p = pHead;//链表的首节点的地址给p 
	while(p != NULL){
		//打印p指向的课程节点的课程信息 
		printf("%4d ", p->Code );
		printf("%-20s ", p->Name );
		printf("%4d ", p->Credit );
		printf("%4d\n", p->Hours );
		
		p = p->pNext;  //p = 后继节点的地址 
	} 
	printf("-----------------------------------\n"); 
}

/************************************************
功能:释放链表
参数:void
返回: void 
************************************************/ 
void F_FreeLink(void);
void F_FreeLink(void){
	TCourseNode* p; //指向节点的指针 

	//释放链表
	p = pHead;//链表的首节点的地址给p 
	while(p != NULL){
		pHead = p->pNext; //链表头指针指向下一个节点 
		free(p); //释放p指向的节点 
		p = pHead;//p指向头节点 
	} 
}

int main(int argc, char *argv[]){
	F_InputData();//输入课程信息,把课程链接到链表尾
	F_PrintReporting(); //遍历链表(打印所有课程信息) 
	F_FreeLink();释放链表
	 
	return 0;
}

第十章 递归问题

递归函数:直接或间接调用自身的函数

在这里插入图片描述

1.递推年龄

#include <stdio.h>
int Age( int k );
int Age( int k ){
	int Value;  //第k人的年龄
	
	//计算第k人的年龄
	if( k == 1 ) Value = 10;
	else Value = Age( k - 1 ) + 2;
	
	//输出计算过程中的中间结果
	printf( "第%d个人的年龄:%d\n", k, Value );
	return Value;//返回第k人的年龄  
} 

int main(int argc, char** argv) {
	//调用递归函数计算第5个人的年龄,并输出 
	printf( "第5个人的年龄:%d\n", Age(5) );
}

2.阶乘问题

#include <stdio.h>
int Factorial( int k );
int Factorial( int k ){
	int Value; //存放k的阶乘 
	
	//计算k的阶乘
	if( k == 1 ) Value = 1;
	else Value = k * Factorial( k - 1 );
	
	printf( "%d! = %d\n", k, Value );//中间结果 
	return Value;//返回k的阶乘 
} 

int main(int argc, char** argv) {
	//调用递归函数计算5!,并输出 
	printf( "5! = %d\n", Factorial(5) );
}

3.斐波那契数列

#include <stdio.h>

int Fibonacci( int k );
int Fibonacci( int k ){
	int Value;  //Fibonacci数列第k项数 
	
	//计算Fibonacci数列第k项数
	if( k == 1 ) Value = 1;
	else if( k == 2 ) Value = 1;
	else Value = Fibonacci( k - 1 ) + Fibonacci( k - 2 );
	
	printf( "Fibonacci(%d) = %d\n", k, Value );//中间结果 
	return Value; //返回Fibonacci数列第k项数
} 

int main(int argc, char** argv){
	//调用递归函数计算Fibonacci数列第5项数,并输出 
	printf( "Fibonacci(5) = %d\n", Fibonacci(5) );
}

4.汉诺塔问题

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

#include <stdio.h>

//功能:把第k号盘子从A座直接移到C座上 
//参数:k(盘子号),A座,C座 

void Move(int k, char A, char C); 
void Move(int k, char A, char C){
	printf("第%d号盘子 %c->%c\n", k, A, C);
}

//汉诺塔问题,把n个盘子从A座借助B座移到C座上
void Task( int n, char A, char B, char C );
void Task( int n, char A, char B, char C ){
	if( n == 1 ) Move(1, A, C); //只有一个盘子 
	else{ //盘子数大于1 
		Task( n - 1, A, C, B ) ;
		Move(n, A, C); 
		Task( n - 1, B, A, C ) ; 
	} 
} 

int main(int argc, char** argv) {
	//调用递归函数移动3个盘子
	Task( 3, 'A', 'B', 'C' );
}

在这里插入图片描述

  • 15
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值