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问题四:综合复杂分支
功能需求分析:
① 输出命令行交互界面的菜单命令选项
② 接收用户输入的菜单命令字母(大小写均可)
③ 根据接收到的命令输出菜单命令字符串 数据(变量)需求分析:一个字符变量存放接收到的命令字符
准备工作:
-
用函数封装“输出命令行交互界面的菜单命令选项”功能: 定义一个函数实现功能①(让程序结构更清晰)
-
定义字符变量:char CommandLetter;
-
学习使用另一个从标准输入获取字符的标准函数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' );
}