C笔记

对弈的c笔记

预科班day01&day02:计算机和VS基础


color /?
0 = 黑色 8 = 灰色
1 = 蓝色 9 = 淡蓝色
2 = 绿色 A = 淡绿色
3 = 浅绿色 B = 淡浅绿色
4 = 红色 C = 淡红色
5 = 紫色 D = 淡紫色
6 = 黄色 E = 淡黄色
7 = 白色 F = 亮白色
system("color 40"); //通过system改变cmd的颜色
system("cls"); //清屏

快捷键:
复制ctrl+c 粘贴ctrl+v
怎么打开CMD windows键+R 打开运行界面 然后键入 cmd 回车
怎么样打开文件 windows+E 可以直接打开文件管理器
怎么回到桌面 windows+D 回到桌面

关于VS快捷键:
编译运行 ctrl+F5
注释多行 ctrl+k+c 取消注释多行 ctrl+k+u
打断点 F9 调试 F10
生成解决方案 ctrl + shift + B
打开文件 ctrl + O
转到定义 F12 转到声明ctrl + F12
帮助文档 F1
全选 trel+A
撤销 ctrl+Z 取消撤销 ctrl+Y
剪切 ctrl+X


*怎么样生成一个别人能用的EXE:
第一步 debug改成release
第二步 在项目属性 C/C++ 代码生成 运行库 改成MT
这个EXE发给别人就可以 直接在对方的电脑上运行


int main()
{
/* 下面代码直接用可能会关机 请看注释。*/
system("shutdown -s at 22:00");
//如果加上at 那么会定时关机

system("shutdown /s"); //一分钟之内关机
system("shutdown -a"); //取消关机计划

//shutdown -s -t time 在time秒后关机
system("shutdown - r");
system("shutdown -a");//取消关机计划
//设定关机计划之后 如果在关机时间内没有及时保存 那么会丢失当前的数据
return 0;
}

 

预科班dany03:基本数据类型

一、基本数据类型简介
分为三类基本数据类型 四种基本数据类型
整数 小数 字母
整型 浮点型 字符型
int float double char

int 对应整型 win32 当前编译环境下 占内存大小 4字节
正整数 负整数 和 0 数据范围 -2^31 - 2^31-1
一字节=8位 4字节=32位
通过第一位的符号位(0表示正数 1表示负数) 剩下31位用来表示数据大小
0000 0000 0000 0000 0000 0000 0000 0000 =0

float 单精度的浮点数(小数)(实型) 4字节
double 双精度的浮点数 8字节
float 数据范围 -1.17*10^38 - 3.4*10^38
double 数据范围 -2.22*e^308 - 1.8*e^308

e表示 10
**精度描述的是 数据存放在内存中的精度 而不是 输出的精度**
double和float输出都是6位

float的精度 6-7位
PI=3.1415926535 用float 3.141592 3.141593
double 的精度是 16-17位


char 字符型 表示 符号 和 字母 1字节 (-128 - 127)
实际上 在计算机里面 是ASCII码表 char类型实际上是整型
ASCII码表上 字母、符号 和 数字 一一对应
字符 '0' 对应的是 48 字母A 对应的是 65 字母a 对应的是 97 大写字母和小写字母差32
数字0+48 =字符 '0'

计算机里面 只有 0 和 1
表示别的 东西 只能通过数字 二进制

二、变量
什么是变量
int a; 实际上是在内存中 开辟一段 长度为 4字节的空间 用来存放 a
在内存中开辟一个存储空间 用来存放不确定的数

怎么定义变量
数据类型 变量名
int float double char
int a;
char c;
float f;
double d;

三、什么是常量
整型常量 一个或多个数字组成 1 10 11 1111
实型常量 十进制的小数形式 1.23
*** .23=0.23 ***
指数形式 e 或者 E 和 阶码组成 阶码只能是整数 e或者E前面必须有数字 后面必须是整数
符号常量
通过一个标识符表示一个常量 称之为符号常量
结合预处理 #define SEVEN 7
全大写 颜色不一样 一般就是系统预留的符号常量

初始化 如果一个变量定义之后 没有初始化 然后 使用了之后
最好的情况是报错 最坏的情况 不报错 但是程序运行出问题

1.通过常量来初始化
2.通过相同类型的变量来初始化

四、变量命名规范
标识符的规范 标识符其实就是变量名
1.只能由字母 数字 下划线 $ 四种组成
26*2 0-9 _ $
摁住shift - 删除键左边的左边那颗键 输入法英文状态下
_________________---------________-

2.必须用字母或者下划线开头
2a $a
a2 _1 _2 _$ aa

3.不能和系统预留的关键字一样
int int; float int;

五、变量命名的方法
1.驼峰命名法
除第一个单词外所有首字母大写
iPhone iPad iPadMini
2.大驼峰命名法
所有单词首字母大写
SummerSeven
3.匈牙利命名法
在变量前加上一个前缀 用来表示类型 1.提示变量的类型 2.增强代码的可读性
int summerSeven;
int i_summerSeven;
int m_Member;
int n_number;

#include <float.h> //用来检测 实型数据的取值范围

int main()
{
printf("float的取值范围:%e---%e\n", FLT_MIN, FLT_MAX); //float
printf("double的取值范围:%e---%e\n", DBL_MIN, DBL_MAX); //double
return 0;
}


预科班day04:二进制

整数的十进制和二进制 怎么转换
正数 原码 反码 补码 都一样
十进制——————>二进制
碾转相除 倒序排列
十进制的 40 二进制 101000
十进制的 55 二进制 110111

二进制——————>十进制
位权相乘 逐位相加
101000---->40
40=4*10^1+0*10^0
101000=1*2^5+0*2^4+1*2^3+0*2^2+0*2^1+0*2^0

int 32位
char 8位 存储过程中 会引入一个符号位 最高位(第一位)作为符号位
符号位用来描述正负 如果符号位 为0为正数 为1为负数

0111 1111:
1+2+4+8+16+32+64=127

负数 反码是人为引入 为了计算的
补码也叫 取反加一码
十进制——————>二进制
1.得到相应的正数的二进制 然后 把符号位 置为1 原码
2.把这个八位二进制 除符号位外 全部变为取反 1变成0 0变成1 反码
3.末位+1 1000 0001 补码
-40
1.得到40 0010 1000 1010 1000 -40的原码
2. 1101 0111 -40的反码
3. 1101 0111
+1
1101 1000 -40的补码
*** -128 计算机特殊规定 1000 0000 ***
*** 补码的作用 在计算机计算的过程中 只有补码 ***
计算机底层 只有加法器 四则运算都没有 只有加法
减法的实质是加法
40-40=40+(-40)=0010 1000 + 1101 1000= 0
0010 1000
1101 1000
10000 0000 =0

浮点数二进制 float 32位
第一位 符号位 中间 八位 无符号 指数位 23位 尾数位
指数位 用来指示 次幂 用来描述小数点移动的位数
用这个得到的指数+127得到 浮点数的指数

100.23 ====科学计数法 1.0023*10^2
0.00123===== 1.23*10^-3
100.23-0.00123
1.0023*10^2- 1.23*10^-3

3.23

1. 先把小数部分和整数部分 分开 11+0.23 分别计算两边的二进制
纯小数的二进制怎么计算
乘二取整数部分 0就是0 1就是1 减掉整数部分
乘尽 就在后面添0 补齐32位
否则就继续算
1.5 0.5*2=1 00000000

取的位数 就是尾数
0.23 0.46 0.92 1.84 1.68 1.36 0.72
0 0 1 1 1 0
然后重新把整数部分和小数部分整合 保留小数点
11.001110---1.1001110
10.0111 1.00111
0.01011000
2.得到指数
11.001110 + 1
让小数点前面 只有一个1 可以不存这个1 可以在小数点后面多存一位 提高了精度
把小数点 移动 让小数点前 有且仅有一个1
移动的位数 就是 实际的指数(往左为正 往右为负) 然后+127 得到 计算机 的指数
11.0001110---1.10001110 小数点左移一位 实际的指数是1
指数是 128---转换成二进制 1000 0000
3.尾数
小数点后面 的 就是尾数
尾数是 100 1110

0 1000 0000 100 1110 ……………… 补满32位

0000 0000 ~~~~ 1111 1111
0~255
-1 -->1
1---> 0

8.76 0 1000 0010 000110000101……
1. 8+0.76 = 1000
0.76 1.52 1.04 0.08 0.16 0.32 0.64 1.28 0.56 1.12
1 1 0 0 0 0 1 0 1
1000.110000101……
2. 1.000110000101…… 小数点 左移了三位 实际的指数是 3 +127 计算计算机的指数 130
1000 0010
3. 尾数 000110000101……
0 1000 0010 000110000101……


-8.76
1 1000 0010 000110000101……


预科班day05:运算符

1.运算符
操作数
在运算符左边的操作数 称之为 左操作数 (左值)
在运算符右边的操作数 称之为 右操作数 (右值)

一个运算符 单目运算符 双目运算符 三目运算符
单目运算符 表示 只有一个操作数 可以在左边也可以在右边
双目运算符 表示 有两个操作数 一个左边 一个右边
三目运算符 表示 有三个操作数

算术运算符
+ - + - * / % ++ --
正 表示是正数
-1 负 表示是负数
-1+3 负1+3

% 取余 左操作数对右操作数做除法 保留余数
5%3=2 2%7 =2 如果能除 就除 然后保留余数
2/7=0 ----2 如果左操作数没有右操作数大 那么结果就是左操作数
(-5)%3= -2 (-5)%3 (-) 5%3= -2
5%(-3)=2 5/(-3)--- -3*-1=3+2=5 5=(-1)*(-3)+2
(-5)%(-3)=-2
***%操作符要求 左右操作数 都是整型***
5%2.0 这个表达式 不符合规则 5/((int)2.0)

++ 自增 (-- 自减)
分为前置自增 和 后置自增 让操作数+1
++a 先自增 再赋值(执行操作)
a++ 先赋值 再自增

位运算符 都要转换成二进制 来做
要从末位开始 对齐 前面没有的 就补0
简单的记忆一下 2的各个次幂的值
^ | & << >>
按位异或 ^ 英文状态下的shift+6
双目运算符 左操作数和右操作数不同时 返回1 否则返回0
0 1 0^1=1 1^0=1 0^0=0 1^1=0
8^5 = 1000 ^ 101 = 1101
16^15=10000 ^ 1111 =11111 =31
15^15=0

位或运算 |
双目运算符 左操作数和右操作数 都为0 返回0 否则返回1
0|0=0 0|1=1 1|0=1 1|1=1
8|5=1101
16|15=11111

位与运算 &
双目运算符 左操作数和右操作数 都为1 返回1 否则返回0
1&1=1 1&0=0 0&1=0 0&0=0
8&5=0
16&15=10000 & 1111 =0

位左移 << char
双目运算符 把左操作数变为二进制 右操作数保留十进制
把左操作数 左移 右操作数 位
***空位补0***
6<<2= 110 << 2 = 110 00 =16+8=24
15<<3=1111 << 3 = 1111 000
char 8位二进制
64<<3= 0100 0000 <<3 == 100 0000 000 = 0000 0000
如果移位过程中 溢出 那么从末位开始 保留 有效长度

位右移 >>
双目运算符 把左操作数变为二进制 右操作数保留十进制
把左操作数 右移 右操作数 位
右移 移出去的直接舍去
高位 会填充符号位
15>>3= 0000 1111>>3= 000 0000 1
-15>>3= 1111 0001 >>3 =1111 1110 001(-2的补码)
怎么样从补码算回原码
正数的原码--符号位置1 --逐位取反--末位+1---补码
补码--末位-1---逐位取反--负数的原码--符号位置0 --正数的原码
-15的二进制 0000 1111--- 1000 1111 ----1111 0000 ----1111 0001
-2的二进制 0000 0010 --- 1000 0010 ---- 1111 1101 --- 1111 1110

关系运算符
计算机里面 判断的时候 非0即1
> < <= >= != ==
都是双目运算符 左操作数的关系和右操作数的关系 满足关系运算符 返回1 否则返回0
1>0=1 1>2=0
!= 不等于 a!=b 如果a不等于b 返回1 否则 返回0
== 等于 a==b 如果 a等于b 返回1 否则 返回0
满足条件 返回1 不满足 返回0
逻辑运算符
&& || !
逻辑与是 双目运算符 如果左操作数和右操作数 都为1 返回1 否则 返回0
a&&b a=1 b=1 a&&b=1
逻辑或是 双目运算符 如果左操作数和右操作数都为0 才返回0 否则 返回1
a||b a=0 b=0 a||b=0
a>b || a<b
逻辑非是 单目运算符 如果右操作数为1 返回0 为0 返回1
!1=0 !0=1 非0即1
赋值运算符
= += -= *= /= %= <<= >>= ^= &= |=
a=5; 右结合性 表达式 从右往左计算
a+=b; --- a=a+b;
a<<=2; a=5; a=5<<2;
三目运算符
非0即1 0为假 1为真
?: 表达式1?表达式2:表达式3
如果表达式1为真 执行表达式 2
如果表达式1为假 执行表达式 3
0?2:3
成员 结构体成员 . ->
数组成员 []
提升优先级的 ()
2.优先级
+ - * / 本身存在优先级
通常的:
单目: + - ++ -- ! ~ 正负
双目: + - * / % >> << ^ & | && || 加减
三目: ? :
单目运算符 高于 双目运算符 高于 三目运算符
在双目运算符里面
算术最高 关系 逻辑


3.举例子

a+b 传入计算机
一系列的指令 包含数据 操作符

预科班day06:进制转换

1.八进制和十六进制

八进制
0 1 2 3 4 5 6 7
用数字前的 O 来表示八进制的标志

十进制 相互转换

从十进制得到八进制: 碾转相除 倒序排列
50 62
从八进制得到十进制: 位权相乘 逐位相加
062 6*8^1+2*8^0=50

二进制 相互转换

从二进制得到八进制 : 把三位整合在一起 表示一位八进制
八进制 最大值是 7 用几位二进制可以表示 7 111
10 110 111 = 267
10000001 = 201

从八进制得到 二进制 : 把一位 换算成 三位 二进制
255 = 010 101 101
111 = 001 001 001


十六进制 在数字前 用ox/OX表示十六进制
0 1 2 3 4 5 6 7 8 9 A B C D E F 大小写都可以
16 --- 10 15 --- f
f+1 --- 10
十进制 相互转换
从十进制得到十六进制: 碾转相除 倒序排列(余数)
50 32
从十六进制得到十进制 : 位权相乘 逐位相加
ff 15*16^1+15*16^0 = 255
10 --- 16
15 --- 16+5=21

怎么打开计算器
windows+r 打开运行窗口 输入calc 打开计算器


二进制 相互转换
从二进制得到十六进制: 把四位二进制 整合成一位十六进制
十六进制最大值是15 二进制来表示15 1111
10 0011 1111 = 23f
23f-1=23e
23f - 10 = 22f
23f - A = 235
abc - d = aaf
abc
- d
aaf

从十六进制得到二进制 :把一位十六进制 转换成四位二进制
在十六进制转换成二进制的过程中 可以直接一位一位写过去

adc = 1010 1101 1100
你们要看题目丫

2.基本输出格式

printf("要输出的东西");
printf("hello world!");
在printf的一对双引号内 直接打要输出的东西

printf("",);
printf("%d",10);
%d 叫做占位符 ,后面的是变量
如果前面没有占位符 逗号后面的变量 没有意义

占位符
int char float double unsigned---关键字 用来描述变量 表示无符号的
%d %c %f %lf %u 控制输出无符号的数字
%f %lf输出的小数点后都只有6位

unsigned 所有位都用来表示数据 没有符号位 0~2^8 0~2^32

3.控制输出
1.占位符
2.转义字符
有一些符号 被系统占用 作为转义字符
如果想输出这些符号 需要打两个
% \

特殊一些组合
\n 表示换行
\t 表示 制表符 键盘上的TAB C语言里面 表示四个空格
\b 表示 退位符 键盘上的删除键
\a 表示 响铃
\0 表示 字符串的结尾
如果想输出符号 配合 \
无刻 上面没有字

3.控制输出格式

%3d %-3d
%.3f
{
int a=10;
printf("%d\n", a); //20 打印出10进制
printf("%x\n", a); //14 打印出十六进制
printf("%o\n", a); //24 打印出八进制

return 0;
}

预科班day07:复习


1.一个数转换为二进制:
int main()
{
int a;
int n = 10;
while (n--)
{
scanf("%d", &a);
printf("%d\n", !(a&(a - 1)));
}
return 0;
}

2.判断一个数是否为2的幂或者被2整除:
int main()
{
int a;
int n = 20;
while (n--)
{
scanf("%d", &a);
printf("%d\n", ((a | (a - 1)) == (a + a - 1)));
}


return 0;
}
*/


1.占位符
char %c 打印单个字符 单引号用来描述一个字符 单引号中如果有多个字符会报错
int %d
float %f
double %lf
八进制 %o
十六进制 %x
无符号 %u unsigned int char
字符串 %s 打印一串字符 双引号用来描述 字符串
输出地址 %p 输出地址的十六进制
%e 科学计数法
%g 在%e和%p之间取一个较短的

定义数据的时候 每次 分配的内存是随机的
定义变量的时候 实际上是去内存中找一个空着的空间 然后放进去

\n 转义字符 表示换行

2.输入时候一些不同于输出
1. scanf("占位符",&变量名);
把一个占位符格式的数据放到变量名内
在输入的时候 必须要 & 只能对内存操作 把数据放到对应的内存里
printf("%d",a);

2.在scanf里面 最后一个不能有\n
如果在scanf里面有\n 需要键入\n
在输入多个数据的时候 中间可以有\n 但是不建议写
最后一个数据 不能有\n
如果有 必须要输入一个一样的 \n

3.输入多个数据的时候 两个占位符之间scanf里面写了什么就必须按照格式输入

4.scanf不会读入空格 如果多个数据 两个占位符之间什么都没有
直接用空格隔开

5. scanf其实是从终端获取数据
从键盘键入数据 会先进入 键盘缓冲区
如果想在输入别的东西之后 继续输入字符
在输入之前 写一行清空缓冲的代码
fflush(stdin); // 清空缓冲区

6.%*4d 表示 不要几位
scanf("%3d%*4d%4d",&a,&b);
printf("%d****%d\n", a, b);

输入多个%c 是不需要用空格隔开
不然某个%c会读到空格

3.scanf scanf_s
安全生命周期

scanf_s("%s", str2, 5); //第三个参数 描述的是 输入多少个字符 注意 \0


fflush(stdin); // 清空缓冲区

输出转换为密码形式:
scanf("%3d%*4d%4d",&a,&b);
printf("%d****%d\n", a, b);

预科班day08:推箱子项目

#include<graphics.h> //图形库的头文件
#include <conio.h> //控制台输入输出
#include <stdio.h> //标准输入输出
#include <windows.h>


//放音乐需要下面两个头文件
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")
#include "resource.h"


/*
0 空地
1 墙壁
3 目的地
4 箱子
5 人物
7 箱子在目的地
8 人在目的
*/


/*

/// 路径有两种描述方式 / \\

1.二维数组 用来描述推箱子的地图

2.函数 把一些代码块 封装起来 可以反复的调用
1+2+3+4+5
int sum(int a,int b)
{
int c=a+b;
}

3.循环 反复的做同一件事情
while(n--)
{
printf("%d",n);
}
4.分支
判断 是否满足条件

*/

//int ch;
//*
//键码 在键盘上 分为两种
//第一种是 字符 ASCII码表上可以找到对应关系的
//第二种是 功能键 上下左右 F1 F2
//通过两个值 第一个是 224 第二个是 真实的ASCII码值

//*/
//while (1)
//{
// ch = getch();
// printf("%d\n", ch);
// printf("---\n");
//}

//地图 下标是0开始 最大到n-1(8-1=7)
int Map[8][8] = {
0, 0, 0, 1, 1, 1, 0, 0,
0, 0, 0, 1, 3, 1, 0, 0,
1, 1, 1, 1, 0, 1, 0, 0,
1, 3, 4, 0, 4, 1, 1, 1,
1, 1, 1, 5, 4, 0, 3, 1,
0, 0, 1, 4, 1, 1, 1, 1,
0, 0, 1, 3, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0, 0
};

void Game_InitMap();//加载地图
void Game_Paint(); //画地图
void Game_Play(); //操作
int Game_Judgment(); //判断游戏结束


IMAGE BackImage, WallImage; //背景 墙
IMAGE Box; //箱子
IMAGE Tag; //目的地
IMAGE Boom; //箱子推到目的地
IMAGE Per; //人
IMAGE END; //奖励

//载入图片
void Game_InitMap()
{
//背景 墙 人 空地 目的地 人在目的地 箱子 箱子在目的地

//背景
loadimage(&BackImage,"res/Background.jpg",550,550);
//墙
loadimage(&WallImage, "res/Wall.jpg", 69, 69);
//箱子
loadimage(&Box, "res/Box.jpg", 69, 69);
//目的地
loadimage(&Boom, "res/Boom.jpg", 69, 69);
//箱子在目的地上
loadimage(&Tag, "res/Tag.jpg", 69, 69);
//人物
loadimage(&Per, "res/xx.jpg", 69, 69);
//最后的图片
loadimage(&END, "res/XiaQi.jpg", 550, 550);

}

//贴图
void Game_Paint()
{
int i, j;
for (i = 0; i < 8; ++i)
//i从0开始 进入循环 判断i<8是否成立 如果满足 进入循环 否则跳出
//执行循环语句 每次执行完之后 让i++
//判断 i是否仍然满足 i<8 如果满足 继续执行 否则跳出循环
{
for (j = 0; j < 8; ++j)
{
/*
0 空地 1 墙壁 3 目的地 4 箱子 5 人物
7 箱子在目的地 8 人在目的
*/
//通过 分支语句 去判断 当前位置是什么 然后贴相应的图片
switch (Map[i][j]) //访问二维数组的方式 通过两个下标
{
case 0: //如果是空地
break;
case 1: //如果是墙壁
putimage(69 * j, 69 * i, &WallImage);
break;
case 3://如果是目的地
putimage(69 * j, 69 * i, &Tag);
break;
case 4://箱子
putimage(69 * j, 69 * i, &Box);
break;
case 5://人物
putimage(69 * j, 69 * i, &Per);
break;
case 7://箱子在目的地上
putimage(69 * j, 69 * i, &Boom);
break;
case 8://箱子在目的地上
putimage(69 * j, 69 * i, &Per);
break;
}
}
}


}

void Game_Play()
{
char ch;
int x, y;
Game_InitMap();
while (true)
{
BeginBatchDraw();//开始批量绘图
cleardevice();
putimage(0,0,&BackImage); //背景
if (!Game_Judgment()) //判断游戏是否结束
{
Game_Paint();
FlushBatchDraw(); //完成未完成的绘制任务

//弹出一个窗口 然后执行相应的操作
MessageBox(GetHWnd(),"闯关成功\n","闯关提示",MB_OK);
putimage(0,0,&END);
FlushBatchDraw();
Sleep(10000); //停留10000ms 10s

closegraph();
exit(0); //退出程序
}
Game_Paint();
EndBatchDraw();

//做人物操作

//首先要找到人物
for (x = 0; x < 8; ++x)
{
for (y = 0; y < 8; ++y)
{
if (Map[x][y] == 5 || Map[x][y] == 8)
{
break; //提前跳出当前循环
}
}
if (Map[x][y] == 5 || Map[x][y] == 8)
{
break;
}
}
//此时 人就在 Map[x][y]

ch = getch(); //获得键盘消息

switch (ch)
{
case 'w':
case 72://向上
if (Map[x - 1][y] == 0 || Map[x - 1][y] == 3)
{
Map[x][y] -= 5;
Map[x - 1][y] += 5;
}
else if (Map[x-1][y]==4 || Map[x-1][y]==7)
{
if (Map[x - 2][y] == 0 || Map[x - 2][y] == 3)
{
Map[x - 2][y] += 4;
Map[x - 1][y] += 1;
Map[x][y] -= 5;
}
}
break;
case 75://向左
if (Map[x][y - 1] == 0 || Map[x][y - 1] == 3)
{
Map[x][y] -= 5;
Map[x][y - 1] += 5;
}
else if (Map[x][y - 1] == 4 || Map[x][y - 1] == 7)
{
if (Map[x][y - 2] == 0 || Map[x][y - 2] == 3)
{
Map[x][y - 2] += 4;
Map[x][y - 1] += 1;
Map[x][y] -= 5;
}
}
break;
case 80: // 往下
if (Map[x + 1][y] == 0 || Map[x + 1][y] == 3)
{
Map[x][y] -= 5;
Map[x + 1][y] += 5;
}
else if (Map[x + 1][y] == 4 || Map[x + 1][y] == 7)
{
if (Map[x + 2][y] == 0 || Map[x + 2][y] == 3)
{
Map[x + 2][y] += 4;
Map[x + 1][y] += 1;
Map[x][y] -= 5;
}
}
break;
case 77: //向右
if (Map[x][y + 1] == 0 || Map[x][y + 1] == 3)
{
Map[x][y] -= 5;
Map[x][y + 1] += 5;
}
else if (Map[x][y+1] == 4 || Map[x][y+1] == 7)
{
if (Map[x][y + 2] == 0 || Map[x][y + 2] == 3)
{
Map[x][y + 2] += 4;
Map[x][y + 1] += 1;
Map[x][y] -= 5;
}
}
break;
}
}
}

int Game_Judgment()
{
int i, j;
for (i = 0; i < 8; ++i)
{
for (j = 0; j < 8; ++j)
{
if (Map[i][j] == 4) //判断有没有箱子在空地上
{
return 1;
}
}
}
return 0;
}

int main()
{

initgraph(550, 550); //创建窗口
/*
//getch(); //获得控制台的输入 getchar获得一个字符 返回

音乐
wav 导入资源文件
添加头文件
如果想要导入资源文件 必须是用wav格式
只能用 playsound
第一种 不导入资源文件
PlaySound("res/BGM.wav",NULL,SND_ASYNC);
//第一个参数 表示 路径 第二个参数 缺省 第三个参数 表示播放的方法
//SND_ASYNC 表示异步播放

第二种 导入资源文件
PlaySound((LPCTSTR)IDR_WAVE1,GetModuleHandle(NULL),
SND_RESOURCE|SND_ASYNC);
*/

PlaySound((LPCTSTR)IDR_WAVE1,GetModuleHandle(NULL),
SND_RESOURCE|SND_ASYNC);

Game_Play();

return 0;
}

正课:

一:进制转换

计算机在内部 只有补码 所有的原码 反码 都是不存在的
计算机只有二进制 通过最高位来表示符号位
正数的 原码 反码 补码都是本身
1.原码 就是数字本身 用0表示正数 用1表示负数
011 1100 0000
2.反码 针对负数 除符号位外逐位取反
1000 1111 -15的原码
1111 0000 -15的反码
正数的反码就是本身
3.补码 通过反码+1
1111 0001 -15的补码
0000 1111 15的补码
1 0000 0000 = 0

正数 三码合一
负数 取反+1得到补码

二进制 八进制 十六进制
*****二进制:
计算机只有二进制
正数:
从十进制得到二进制:碾转相除 余数倒序排列
23 的二进制 0001 0111
二进制得到十进制:
每一位乘以当前的权值 求和
123=1*10^2+2*10^1+3*10^0
从末尾开始 用第n位的数值 乘以2的n-1次方
0001 0111
1*2^0+1*2^1+1*2^2+0*2^3+1*2^4

负数:
1.以正数为模型 符号位置为1
2.除符号位外 逐位取反
3.+1
-23
0001 0111 23的原码
1001 0111 -23的原码
1110 1000 -23的反码
1110 1001 -23的补码

*****浮点数:
3.14
float 32位
double
1.分成整数部分和小数部分 分别转换成二进制
2.把整数部分的二进制和小数的连在一起 保留小数点
3.移动小数点让小数点前只有一个1 记录小数点移动的位数
n n+127的操作 得到 一个无符号的 指数
无符号 第一位不表示正负表示大小
0~255
4.保留余下的小数点后的位数 标记为 尾数
1位符号位 8位指数位 23位尾数位

3.14
3 = 0000 0011
0.14 小数部分 反复乘2 取整数部分 顺序排列
0010 0011
一般会乘不尽 要补满23位尾数 一般是后面添0

32位
111.0010001 左移两位 +2
0.00011000 右移四位 -4
-4+127=123
0111 1011
左移标记为正数 右移标记为负数
11.00100011
1.100100011 左移一位 然后舍去第一位1
指数 +1+127 =128 转换成二进制
指数 1000 0000

1001 0001 1 剩下的尾数
符号位 指数位 尾数位 补的0
0 1000 0000 1001 0001 1000 0000 0000 000
-3.14 在3.14的基础上 符号位变为1
1 1000 0000 1001 0001 1000 0000 0000 000

八进制和十六进制
n进制 以n为进位标志
0~7 0~15
111 1111
第一个 八进制和十六进制 和二进制的相互转换
第二个 八进制和十六进制 和十进制的相互转换

八进制 用三位二进制来描述 在八进制数字的开头0
只需要一位八进制 变成三位二进制就可以了
二进制 转换成八进制 三位二进制组合在一起
0001 0111
027 --- 2*8^1+7*8^0=23
0001 0111
0X17 --- 1*16^1+7*16^0=23
如果不够 往前面 添0
011 000 111
027 --- 010 111
0X17 --- 0001 0111


****所有的C语句都以 ; (英文状态下的分号)结尾
输出 :
printf("输出的内容");

printf("格式控制",输出列表);
格式控制 --- 占位符
%d 整型
%c 字符型
%f float
%lf double
%o 八进制
%x 十六进制
%u 无符号
%p 输出地址 得到十六进制 要配合&使用
%s 字符串

%e 科学计数法
%g %e和%lf之间最短的一个

\ 转义字符
\a 响铃
\b 退位 删除键
\n 换行
\t 表示制表符 TAB 四个空格

下面这串代码很危险 不要碰
可以让朋友玩
while(1)
{
printf("HELLO\a");
}

*****标识符:给变量起名字
我们存放变量是需要用标识符
x+y=10
1.只能由
下划线 _ 数字 字母(区分大小写) $ 四种组成
2.必须以 下划线 或者 字母 开头
3.不能和系统保留的一些关键字冲突
32个关键字 看一下分别是哪几个
_summerseven 0uguang Summer7
int float

标识符的长度
c89是31位
c99是63位

一些全大写的 一般是系统预留的 宏

float c = 3.14f;
//如果没有f 自动转换成 double类型
//float 4字节 double 8字节
// 计算机的隐式转换
printf("%o\n",c); //直接输出
printf("%.3f\n", c); //控制输出 三位小数
printf("%10.3f\n", c); //右对齐
printf("%-10.3f***\n", c); //左对齐

return 0;
}

二:基本数据类型

1.常量变量
常量: 有三种
浮点型 (实型)
整型常量:一个或多个数字组成的
1 11 111
实型常量:
1.所有的小数
2.科学计数法描述的 带指数的
在计算机 十进制和阶码标志(E/e) 和阶码
1e3 阶码必须是整数 10^2 10^3 //10^0.1
-1.24 0.24 .23
1E3
符号常量:
用标识符来表示一个常量
HEllo World Hello hell

变量 :
为什么要定义变量:
想要去描述一个可变的同一数据类型
int a; a=84; a=85; a=87;
定义变量的格式:
数据类型 标识符
数据类型用来声明变量的格式
标识符就是名字

实际上会在内存申请一块空间
空间名字叫标识符
空间大小就是数据类型所占的大小

变量的作用域:
局部变量的作用范围 是 {}
全局变量的作用范围是整个文件

2.基本数据类型
int float double char
整型 单精度浮点型 双精度浮点型 字符型

数字 一共就只有十个

1字节=8位 1kb=2^10b 1mb=2^10kb
32G= 2^10 2^10 2^10
30G= 1000 1000 1000

整型家族
int 有符号整型 占四个字节 32位
数据范围 : -2^31 ~ 2^31-1

short 有符号短整型 占两个字节 16位
-2^15 ~ 2^15-1

unsigned int 无符号的整型 占四个字节 32位
0 ~ 2^32-1

long long 长整型 占8个字节 64位
-2^63 ~ 2^63-1

在不同的编译器下可能是有区别的
long 和 int 是一样的
long 也是四字节 32位
有些编译器下 long也是8字节

1111 1111 1111 1111 1111 1111 1111 1111

浮点型 小数点后 取值范围 (负-正)
float 单精度 4 6~7位 3.4*10^38
double 双精度 8 16~17位 1.7*10^308
float 只有32位 23位尾数
9 1001 999999
double 64位
52位尾数位
float double 取舍
对精度要求特别高 数据很大的时候
0.000001
运算速度要求高的时候 精度不是很高

char 字符类型
ASCII码表 :让计算机识别字符 通过数值的形式
计算机里面只有二进制 描述除数字之外的东西
char 字符 --- 数值 一一对应
-128 ~ 127
a 97 A 65 '0' 48
实际上 char 也是整型 占一个字节 -128 ~ 127
字符常量 'a' 'A' 单引号
字符串常量 "Hello" 双引号
在单引号内 可以存放最多四个字母 不论大小写
如果向打印对应的符号 有两种方法

直接打印%c 对应的字符
打印%c 对应的ASCII码值

 

sizeof() 运算符
返回 ()内的数据所占内存的大小
()内可以直接放数据类型名 可以放变量名
都会返回相应的大小


错误 是不能通过编译的
警告 是可以通过编译 在特殊的情况下 是会出错的

 

3.输入输出


4.标识符
字母 数字 下划线 $ 四种
$ $ $ $ $
不能和关键字冲突
必须以下划线或者字母开头

命名方法
驼峰 除第一个单词外 所有首字母大写
iPhone iPadMini
大驼峰
SummerSeven OuGuang
匈牙利
加前缀 来描述变量的属性
int i_summer
char c_Summer

三:运算符


根据运算符的种类:
算术运算符 位运算符 关系运算符
赋值运算符 逻辑运算符 条件运算符

操作数 :
在运算符的两侧可能会有数值
左侧 左操作数 或者 左值
右侧 右操作数 或者 右值
1+2 +运算符 1为左操作数 2为右操作数

根据运算符的操作数个数
单目运算符 一个操作数
双目运算符 两个操作数
三目运算符 有且只有一个 ?:
a?b:c


算术运算符
+ - + - * / % ++ --
正号 负号 自增 自减
% 取余 左操作数 对右操作数 取余
做除法 保留余数
5%2=1 -5%2=-1 5%(-2)=1
-7%2=-1

++ 自增 自己增加1
前置自增 ++a 自增符号在操作数前面
先执行自增 再赋值运算
后置自增 a++ 自增符号在操作数后面
先赋值运算 再执行自增

-- 自减 自己减少1

位运算符
<< >> | & ^ ~
左移 右移 或 与 异或 取反
a<<b 把左操作数 转换成二进制 左移 右操作数位数
相当于 在a的末尾填b个0 然后舍去前面的b位
在不越界的情况下 a*2^b 位左移 其实是一个乘二的操作

a>>b 把左操作数 转换成二进制 右移 右操作数位数
算术右移 计算机做的是算术右移
如果为负数 符号位1 在a前面补b个符号位
会保留正负
逻辑右移
不管正负 补0

在计算机中 非0即1
按位或 | 左右操作数 只有同时为0 才返回0 否则为1
a|b=
0|0=0 0|1=1 1|0=1 1|1=1
左右操作数 各自转换成二进制 然后做或运算
逐位相或 按照规则 该是1就是1 是0就是0
-1|-1 1000 0001 1111 1110 1111 1111
1111 1111 | 1111 1111 =1111 1111 =-1
3|7=
0000 0011 | 0000 0111 = 0000 0111 = 7
如果一个负数 和一个整数 做或运算 结果是否一定为负

按位与 & 左右操作数 只有同为1 才返回1 否则返回0
0&0=0 0&1=0 1&0=0 1&1=1
左右操作数 各自转换成二进制 然后做与运算
逐位相与 按照规则 该是1就是1 是0就是0
3&7=
0000 0011 & 0000 0111 = 0000 0011 = 3
-1 & 7 =
1111 1111 & 0000 0111 = 0000 0111 = 7
如果 int 就是32位 longlong 64位

按位异或 ^
只有左右操作数 不同 才返回1 否则返回0
1^0=1 0^1=1 0^0=0 1^1=0
左右操作数 各自转换成二进制 然后做异或运算
逐位异或 按照规则 该是1就是1 是0就是0
3^7=
0000 0011 ^ 0000 0111 = 0000 0100 = 4
-1 ^ 7 =
1111 1111 ^ 0000 0111 = 1111 1000 = -8
1111 0111 1000 1000

取反 ~ 全部取反
转换成二进制 所有位 0变1 1变0
~0 =
~0000 0000 =1111 1111 =-1

关系运算符
> < <= >= == !=
> < <= >=
if(3<a<5)
{
printf("hello world");
}
关系运算符比完之后 会返回1 或者0
true false
a=0 3<a =0 0<5=1 if(1){}
a=4 3<4 =1 1<5=1
3<a && a<5

== 判断 如果左边等于右边 返回 1 否则返回0
3==3 ---- 1
if(*p==NULL) 判断赋值是否成立
if(*p=='\0') '\0'是字符串结尾的标志 用来判断一个字符串是否到结尾
if(map[x][y]==5)
break;

if(5==map[x][y])
把常量放在左边 避免==写成一个等号
赋值 右结合性 如果左边为常量 常量是不可改变的

!= 不等于 如果左边等于右边 返回0 否则返回1
1!=2 ---- 1

if(4!=map[i][j]) 判断游戏结束的时候 是否有空箱子

赋值运算符
= -= += *= /= %= <<= >>= ^= |=
赋值运算符
= 右结合性 从右边开始往左边运算
把右操作数的值给左操作数
a=3
a+=3; a=a+3=6;
a*=3 a=a*3;
a>>=3 a=a>>3;
a右移三位 然后把右移三位的值 赋值给a
逻辑运算符
&& || !
逻辑与 && 左边和右边同时为1 才为1 否则为0
一般的 逻辑运算符会和关系运算符结合
a=3
a>=3 && a<5 [3,5)

逻辑或 || 左边和右边同时为0 才为0 否则为1

逻辑运算符的短路
从左往右的时候 如果计算到当前位置 可以让逻辑运算符返回值 那么 不会执行剩下的语句
&& 如果 左边就返回0 那么不会去执行右边的语句
|| 如果左边就返回1 那么不会执行右边的

逻辑非
!a a为1 返回0 如果a为0 返回1

三目运算符
表达式1?表达式2:表达式3
判断表达式1 是否为真
如果为真 执行表达式2
否则 执行表达式3

a b c
max=a>b?a:b;
max=max>c?max:c;

未定义式
a=i++ + i++ ;
a=++i+++i+++i;
++i;++i;++i;a=i+i+i;
a=i+i;
i++;
i++;

C标准里面 根据编译器自行处理

逗号表达式
,
a=3,4,5;
如果 没有括号 返回第一个
如果 有括号 返回最后一个


() [] . -> {}
() 1.提升优先级 2.形参列表
[] 数组的成员 访问 map[i][j]
{} 1.作用域 2.定义域
. 分量运算符 结构体变量的成员

优先级
1.不要刻意记优先级
2.可以通过括号 提升想要先做的
3.考试 面试
单目>双目>三目

四则运算符(算术) 关系 逻辑 == !=

四:分支语句

分支语句

1.
if(表达式1)
{
语句块;
}
判断表达式1的真假 如果为真 执行语句块
否则 跳过

表达式可以是一个变量
判断 变量是否为1
如果是一个表达式
判断表达式的返回值

2.
if(表达式1)
{
语句块1;
}
else 否则
{
语句块2;
}

如果 否则

3.
if的嵌套

当嵌套的时候 要注意 每个else是和相关联的 if在一起

else一定要注意逻辑 是和相应的if匹配的
如果匹配错 逻辑就会错误

else是必须跟在if后面的

4.
if(表达式1)
{
语句块1;
}
else if(表达式2)
{
语句块2;
}
else if(表达式3)
{
语句块3;
}
else
{
语句块4;
}

划分自然数
负数 0 正数
if a>0
正数
else if a<0
负数
else
0
5.
if(表达式1)
{
语句块1;
}
if(表达式2)
{
语句块2;
}
多个单独的if等级是一样的 相互不影响

if 注意事项:
1.if语句后面不要+分号 如果一定要+分号
如果后面没有else 可以在花括号结尾加分号 表示空语句
如果有else 不能加分号
2.if 后面的花括号是可以省略
如果是一个语句
但是 如果是声明变量 就必须要括号
必须加花括号
3.if 后面可以跟任意个语句
必须在花括号内
4.非0即1 避免表达式 因为码代码的失误造成永真或者永假
if(a==1) {} if(a=1) {}
把常量写在关系运算符的左侧
因为常量是不可修改的 常量不能为赋值运算符的左值
5.if语句里面 定义的变量 作用域只在{}内
只能在当前的语句块内访问
6.if的表达式里面 可以有多个任意表达式
可以是一个简单变量
可以是一个表达式
也可以是 多个逻辑语句 组合
0|| 1 ||asda ||sad
1&&1&&1&&0&&asdjah&&asda

判断闰年
闰年有两种
能被4整除 但是 不能被100整除
能被400整除
1900 %4==0
1900%400!=0


switch

switch(变量名)
{
case 常量表达式1:
语句块1;
break;
case 常量表达式2:
语句块2;
break;
}
用switch里面的变量 去匹配case的情况
如果匹配成功就执行case之后的语句

int a=3;
switch(a)
{
case 3:
printf("Hello World!\n");
break;
}
a 进入switch之后 会去匹配相应的值
如果 匹配成功 就执行 相应的内容

在case后面 必须有break
用来跳出 当前 switch

开关 转换 鞭子
break 只能用于循环或者 开关语句 也就是 switch

会逐个判断
如果匹配到就会执行
如果全部判断完 没有匹配成功就会结束

两个case的值 不能相同

break跳出
如果没有break case语句的穿透性
如果匹配成功 而且没有break
会直接执行下一个case 并且不需要匹配
直到break default 也会穿

switch 的注意事项:

1.switch 表示式内 必须是 整型类型的表达式
会有别的类型 但是 实质上都是整型
char 枚举
int 常量 char 字符常量
2.case 后面 只能接常量表达式
并且 不能重复
任一case的值都不相同
3.switch 小括号后面不能加分号
如果一定要加 只能加在花括号后面
4.default 可以写在任何位置
不影响执行顺序
不管写在哪里 匹配失败都会执行
5.case之后的语句可以不加花括号
也会全部执行
6.在case后面第一个语句 是不能直接声明变量的
7.在case后面 非第一个语句是可以声明变量的
8.如果 一定要在第一个语句声明变量
那么要用花括号 把整个case的内容括起来
9.case 中 如果不加花括号声明的变量 其他case块也能访问

如果前面的case 声明了一个变量 并且不在花括号内
那么 后面的case 可以直接拿来使用 不需要声明
要初始化 否则会显示未初始化的局部变量

如果后面不初始化 那么会访问一个垃圾值
前面的case 如果访问后面定义的变量
那么会显示 未定义的标识符 也就是说 是不可以的
如果前面的case 定义了一个变量
后面在定义同一个变量名的时候 就会显示 重定义
可以多次赋值

未初始化的局部变量
未定义的标识符

五:循环语句

goto
无条件执行 直接跳转到相应的语句
任何条件都会执行 只要程序走到goto这一行

可以在语句前面 给出一个标签
一个名字 :
不论这个标签在哪里 只要goto执行了 都会执行标签语句

不建议使用goto

有一个好处: 可以直接跳出多重循环

while
while(表达式)
{
循环体;
调整语句;
}
1.判断表达式是否成立 如果成立执行循环体一次,回到表达式
判断是否成立
2.表达式不成立 跳过循环

循环内
判断条件
循环体
调整语句 调整语句 调整的是判断条件的变量
判断条件
可以是变量 判断变量为0还是1
可以是表达式 判断表达式的返回值
调整语句
可以写在循环体内 也可以直接写在判断条件上

break
跳出循环 直接执行循环后的语句
continue
结束当前次循环 返回到判断条件
只能作用于 最内层循环
tips:

1.要注意 不要让表达式 进入永真或者永假 防止出现死循环
2.while括号后面 不能加分号
如果在括号后面 加分号表示循环体为空语句
有时候 会遇到 判断语句内做了循环体的操作
循环体就为空 换一行 打一个分号 表示循环体为空
3.调整语句不能忘
4.大括号可以省略
如果省略 循环体 只能为一个语句
这一条语句不能是声明变量 会引起重定义
如果 循环体有多个语句 不能省略

错误 分为两种
语法错误: 编译器会报错

逻辑错误: 语法正确 但是 不能执行相应的功能

循环嵌套
外层循环执行一次 内层循环执行n次
外层循环想执行第二次 必须等到内存循环执行完

do while
do{
循环体;
}while(表达式);

while后面的小括号后 必须有分号

先执行循环体 ,然后判断表达式 是否为真
如果为真 继续执行循环体 否则跳出循环

dowhile和while的区别 一定会执行一次循环体
不论表达式 是否为真

MSDN 微软的帮助文档
在线版本 一个函数 不了解用法 选中函数 F1
如果浏览器没有带翻译功能 就是全英文的
离线版本 14G

for

for(表达式1;表达式2;表达式3)
{
循环体;
}
表达式1 初始化条件
表达式2 判断条件
表达式3 调整语句
先执行初始化条件,把循环变量初始化,判断条件是否为真
如果为真 执行循环体,执行调整语句,再判断条件是否为真
如果为假 就跳出循环

tips:
1.for 构成死循环
第一个 没有调整语句 判断条件不会改变
第二个 判断条件永真或者永假
第三个 没有判断条件

2.for 相比于 while的优势
如果要看明白一个while是怎么执行的
看明白一个for 只需要看小括号内
while 转汇编的时候 比for少一行代码

3.for循环的时候 表达式123都可以缺省
但是 分号不能缺省

什么时候用什么循环
循环是有适用的

知识点回顾:
goto:无条件跳转
while:避免永真永假
do while: 一定会先执行一次循环体
for: for(;;) 即使表达式缺省 分号不可省


六:数组1

数组:
数组:一堆具有 相同数据类型 的数据 的集合
如果一个标识符被变量用过了
那么数组就不能再用了

访问数组元素:
数组名[访问的位置(其实就是下标)];
数组的下标 是从0开始的
int a[5]; a[0] a[1] a[2] a[3] a[4]
0 ~ 数组大小-1
在内存中是连续的
a[2]=5;

初始化:

int a;//定义一个整型变量 标识符 变量的名字为a
int brr[5]; //定义一个整型的数组 标识符 数组的名字为b 大小为5
float c[20];//定义了一个float的数组 名字叫c 有20个元素

数组元素的初始化:
int a[5] = {1,2,3,4,5};//可以按顺序对整个数组赋值
int a[5] = { 0 };// 只对第一个元素赋值 缺省其他元素的值
int a[6] = { 1, 2, 3, 4, 5 };
int a[5] = {};//可以缺省所有值
int a[] = {1,2,3,4,5};//可以在前面缺省数组大小,但是在初始化列表给出所有元素的初值 int a[5] = {1,2,3,4,5};
int a[] = {}; 编译器:你要定义什么?---错误
int a[5];//不进行初始化就调用,会输出垃圾值---没意义
int a[5];

数组的赋值 只能是逐个赋值:
// 1. 逐个枚举 单独 赋值
// 2. 循环 逐个赋值

int a[5] = { 0 };
printf("%d\n", sizeof(a));//得到的是 数组的大小 4*5=20
printf("%d\n", sizeof(a) / sizeof(a[0]));//sizeof(int)


字符串 是一个特殊的字符数组
默认最后一个位置似乎\0
字符串数组 字符数组数组

int main()
{
char a[10] = { "helloworl" };//只能存储9个最后一个为'\0'
//a[9] = 'l';
char b[10] = { 'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', ' ' };
//能存储10个

char c[] = { "helloworld" };
//针对 字符数组 如果用字符串去初始化 那么要注意
// 字符串的结尾 默认为 '\0' 占一个位置
// 如果数组大小为 10 那么只能用9个单位长度的字符串去初始化
//如果用字符去初始化 那么遵照数组的原则


冒泡排序:
1.从头开始,相邻数据进行比较,如果前者比后者大交换,否则不交换
2.从第一对开始,比较到最后一对为止,最大的数据就到最后了
3.对未排序的数 继续上述操作 重复1 2
4.得到有序数组

3 2 5 4 1
0 1 2 3 4
第一次:2 3 5 4 1
2 3 5 4 1
2 3 4 5 1
2 3 4 1 5
第二次:
2 3 4 1 5
2 3 4 1 5
2 3 1 4 5
5个数字
第一轮比较4次 得到最大值
第二轮比较3次 得到次大值
第三轮比较2次 得到第三大值

// 冒泡排序
int a[10] = { 13, 590, 650, 28, 19, 120, 567, 73, 90, 278 };
printf("before 排序:\n");
for (int i = 0; i < 10; i++)
{
printf("%d\t", a[i]);
}
printf("\n");
for (int i = 0; i < 10; i++)
{
for (int j = i + 1; j < 10; j++)
{
if (a[i]>a[j])
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
printf("after 排序:\n");
for (int i = 0; i < 10; i++)
{
printf("%d\t", a[i]);
}
printf("\n");

交换两个数的方法:
加减法:
a = b - a; //a=b-a
b = b - a;//b=b-a =b-(b-a)=b-b+a;
a = a + b;
乘除法:
异或法:
0^0=0 1^0=1 a^0=a a^a=0
5^0 = 0000 0101 ^0000 0000 =0000 0101 = 5
5^5 = 0000 0101 ^0000 0101 =0000 0000 = 0
a = a^b;
b = a^b; // b=a^b=a^b^b=a
a = a^b; // a=a^b=a^b^a=a^a^b=b


七:数组2

二维数组:

*****
*****
*****

以行为主序 每读一行 读5列

int a[3]={*****,*****,*****};
int b[5]={*****};
b[0]~b[4]

a[3]={b[5],b[5],b[5]};
int a[3][5]={*****,*****,*****};
a[0][0] ~ a[2][4];

int a[2][4];
a[1][3];
****
****
通用定义方式:
数据类型 数组名 [第一维][第二维];
int a[3][5]; 三行五列
数组名可以理解为是 数组的首地址 数组第一个元素的地址
地址不可变

内存:a[15]
连续的 第二行会接在第一行的地址后面
实际上是同一行的
第二行的第一个元素和第一行的最后一个元素是相邻的
&a[0][4] +4 = &a[1][0]

int a[6]={1,2,3,4,5,6};
&a[2] = &a[1]+sizeof(int)
访问:
访问到具体元素
a[0][0]; 第一行第一列
a[1][0]; 第二行第一列
如果要访问元素 需要给出两个下标

一维数组的一维数组
a[3] 3*a[5];
int a[3][5];
如果要访问某行的第一个位置 表示某一行
a[0] ---- 第一行
a[1] ---- 第二行
a[1]实际上也是一个数组
a[1][2]

a[3][5] ---- 3*b[5];
a[0] a[1] a[2]

C语言默认行主序

初始化
用一对花括号 表示一行 看着方便
定义二维数组的时候 只有最高维 可以缺省
如果想跳过一行不赋值,必须用{0} 表示 默认为0
, , 之间必须有数值 不能是空格

数组名 可以理解为 是数组的首地址
printf("%p\n", &c[1]);
printf("%p\n", c[1]);
printf("%p\n", &c[1][0]);

根据概念去理解 然后去计算
int arr[4][2] = {1,2,3,4,5,6,7,8
}; //假设arr数组的起始位置是1000
//arr
//arr + 2
//arr[3]
//arr[2] - 1
//& arr[1][2]

printf("%d\n", arr);
printf("%d\n", arr + 2); // 2*2*4
//2*2*sizeof(int) 2*2*4
//2*sizeof(arr[0]) 2*4*2
printf("%d\n", arr[3]); //地址 3*2*4 arr+3 arr[3]+0
printf("%d\n", arr[2] - 1); // 2*2*4-4
// a[1][1]
如果只给出数组名 那么加减法作用在第一维上
如果给出第一维坐标 那么加减法作用在第二维上

arr[4][2];
printf("%d\n", &arr[1][2]);
arr[2][0];

printf("%d\n", &arr[2][-1]);
arr[1][1];
arr[4][2];


八:指针一
在32位系统下 不论是什么指针 都是四个字节
如何定义指针
指针类型
指针没有初始化 访问有两种情况

int a; //定义一个变量 整型 在内存中申请一块空间
int * pa; //指针的大小
pa = &a; //把a的地址放到pa里
*pa; //解引用 返回 指针指向的地址的值
a = 3;
printf("%d\t%d\n", a, *pa);//3 3
printf("%p\t%p\n", &a, pa);//3 地址

3.14存放在内存中也是二进制 那么按照整型的读法 是一个大整数
为什么能够反馈出来是3.14而不是一个整数
因为地址是有类型的
不能简单的通过看值来判断类型

1. 指向一个不可用的地址,报错 内存错误
2. 指向一个有数据的地址,一个合法的地址

空指针 NULL nullptr
指针必须是在变量的地址上 不能直接访问地址的数值

char ch = 'a';
char *cp = &ch;
ch; // 'a' 97
cp; //ch的地址
&ch; // ch的地址
*cp;// 'a' 97
*cp + 1; // b
*(cp + 1); // 下一个地址的值
printf("%p\t%p\n", cp, cp + 1);

int a = 5; // 0x10
int *pa = &a;
*pa + 1; //6
*(pa + 1); // 0x14
//当前地址+1*sizeof(int)
printf("%p\t%p\n", pa, pa + 1);
printf("%d\t%d\t%d\n", *pa, *pa + 1, *(pa + 1));


内存四区:
代码区: 放代码的地方
全局,静态,常量数据区:
栈区:数组,变量 不需要手动释放 会随着程序的结束自动结束
堆区:需要手动申请手动释放,malloc 申请动态内存

**不允许对NULL指针解引用

指针 的运算
指针只有两种运算:
算术运算: + -
+: 在指针的基础上 + 整型
+整型*sizeof(数据类型)
-:原则上,任意指针之间都可以做减法操作
但是,如果要有意义,只有同一个数组的指针做减法才有价值
同一个数组的指针相减,返回中间的元素个数
关系运算: < > <= >=
用指针之间的关系运算来返回帮助判断循环或者分支


二级指针
指针是指向变量的变量: 存放的是变量的地址
二级指针: 指针的指针 ,存放的是 指向变量的指针 的地址


int main()
{
int n = 5; //定义一个变量 叫n 是个整型
int *pn = &n; //定义了一个指针变量 叫pn 指向整型
int **ppn = &pn;
int m = 7;

*pn = 67;
*ppn = &m; //修改pn的指向
//ppn=&pn;
//*ppn=pn=&n;

九:指针二

数组指针 指针数组

一级指针可以指向二维数组 那为什么要引入数组指针?
为了提高代码的可读性

数组指针 数组的指针 int (*p)[5] 指向列数固定的二维数组

指针数组 指针的数组 int *p[10] 一个元素个数为10个的数组
每一个元素都是指针


常量指针 指针常量 常量指针常量

常量指针 指向常量的指针
指向可以改 但是不能通过指针修改指向的值
常量指针只能规定当前指针不能修改指向的值,不能确保别的指针不改

指针常量 指向变量的不可修改的指针
指向不能改 但是可以通过指针修改指向的值

常量指针常量 指向常量的不可修改指向的指针
不可以改指向,也不可以改指向的值


数组名不是指针 数组名不是指针 数组名不是指针
为什么数组名可以代替地址
在有数组名参与的表达式里面
大多数情况下 会为数组名生成一个首地址的指针常量
两种情况下不是:
sizeof(a)/sizeof(int) 当做整个数组来用


int main()
{
int a[5] = { 1123, 2354, 23, 75, 12 };
int *pa = a;
&a 当做数组来使用
pa = &a[0];
int *p[10];


数组指针
for (int i = 0; i < 5; i++) //输出行
{
for (int j = 0; j < 3; j++) //输出列
{
printf("%d\t", *(*(pb1 + i) + j));
// 第一次解引用 找到行
// 第二次解引用 找到列
}
}
数组指针:
可以指向所有列为5的数组
可以指向二维数组
必须一模一样 列数必须相同


char *keyword[] = {// 这是一个指针数组
"do",
"for",
"while",
"return",
"switch"
};


int main()
{
int a = 5; //定义一个变量
int *pa = &a; //定义一个 普通的指针
//const //用来表示 不可改变的量 不可修改的量
int const * pa1=&a; //常量指针
const int *paaa; //这个形式和上面一样

int b = 7;
int * const pa2=&a; //指针常量

printf("%d\n", a);
*pa=4; //可以通过指针修改指向的数值
printf("%d\n", a);
//*pa1 = 7; //常量指针 不能修改指向的值
printf("%d\n", *pa2);
*pa2 = 7;
printf("%d\n", *pa2);
printf("%d\n", *pa1);

int a = 5;
int const * const pa;
常量指针常量

return 0;
}


字符串结尾是 '\0' 所以可以用来判断是否是结束
char str[30] = "Tanzhou Liushuai Summerseven";
char *s = str; //用指针指向数组
int len = 0; //初始化长度为0
while (*s++ != '\0') //判断当前字符串是否到结尾
{
len++;
}
printf("%d\n", len);
return 0;

十:指针三

实现:strcpy

int main()
{
char str1[20] = "Hello SummerSeven";
char str2[20];
char *s = str1;
char *s2 = str2;
//for (int i = 0; *s != '\0'; i++)
//{
// *s2 = *s;
// s++;
// s2++;
//}
////没有字符串结尾的\0
//*s2 = '\0'; //手动的在字符串结尾添加 \0 强行结束

while ((*s2++ = *s++) != '\0')
;
//首先执行++ 然后执行* 然后执行= 然后执行!= 然后循环
//实际上 循环条件 判断的是 s2里面的 \0 而不是s 的\0
//所以能把\0也拷贝过去

puts(str2);

int a;

//== 运算符 获取到的是 scanf函数的返回值 而不是内容
// scanf输入成功就是1 失败就是0
if (scanf("%d", &a) == 1)
{
printf("Hello world\n");
}

return 0;
}


int main()
{
//int a[5] = { 1234, 123, 45, 5467, 658 };

//printf("%d\t%d\n", a[2], 2[a]);
//// 讲这些东西的目的 要认识它 要知道它不好 不能写

//// int a[10]; a[n]
//// *(&a[0]+sizeof(int)*n)
//// int *p=a; p=&a[0] *(p+sizeof(int)*n)
//// *(a+2*4) == *(2*4+a)

//int b[5][5] = { 0 };
//printf("%d\t%d\n", &b[3][0], &b[3, 4]); //不一样
//printf("%p\t%p\n", &b[4][0], &b[3, 4]); //一样
// 逗号表达式 运算符
int a = 0;
a = 3, 4, 5; //a=3
a = (3, 4, 5); //a=5
printf("%d\n", a);

return 0;
}


int main()
{
//输入输出的时候
// %*nd 自动丢弃 n位
//int a, b;
//scanf("%d~~~~~~%d", &a, &b);
//printf("%d\t%d\n", a, b);

int a[3][3];
//char c = getchar();

int *pa[3]; //这是一个数组
pa[0] = a[0];
pa[1] = a[1];
pa[2] = a[2];

for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", (*(pa+i)+j));
}
}

for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%d\t", *(*(pa + i) + j));
}
printf("\n");
}

 

return 0;
}

十一:函数一

函数 基本形式

返回值类型 函数名(参数列表)
{
函数体;
}

返回值类型 规定函数返回的值的类型
函数名 调用的时候要用的名字
参数列表 传参给函数的时候 要传的参数
函数体 函数的功能
{} 作用域

定义

int Myadd(int a,int b)
{
int sum=a+b;
return sum;
}

函数名 随便起 遵循 标识符的规则
返回值类型 规定了函数的返回值的类型
void int double float char int * 结构

为什么要返回值 根据写函数的需求来定的
如果是有返回值的函数 真函数
返回值本身就是根据需求写的

如果有返回值 可以参与语句 可以直接成为语句
如果没有返回值 可以直接成为语句

return ---- 功能上 类似 循环中的break
如果函数执行到return 会直接返回
return 0 正常的返回

声明
返回值类型 函数名 (参数列表) ;

如果 函数的定义写在 调用之后 那么必须在调用之前 写声明

声明的时候 可以不写变量名
因为声明函数时 只关注类型

定义的时候 必须写变量名

tips:
函数体内 可以定义变量 没问题

如果函数没有要求传参 最好在参数列表写上void

如果没有给出返回值类型 , 函数默认返回int 而且可以执行

C语言的函数所有参数传递 都是传值调用.
传值调用 ---- 传址调用

传入函数为地址的值的时候
做交换操作的时候
c= &a d= &b

1.传递给函数的标量参数是传值调用
2.传递给函数的指针参数是 把地址的值 传值调用


int max(int a, int b, int c) //返回值类型为 int 函数名叫 Myadd
// 参数列表为 int a, int b 需要传两个参数 都是int


函数参数列表 是形参 形式参数
main内 调用函数内 是实参 实际参数
形参不会影响实参的值

void Myswap(int *a, int *b) //定义一个交换函数
{
int temp = *a;
*a = *b;
*b = temp;
//printf("%d\t%d\n", a, b);
}


void Myswap1(int *a, int *b) //参数列表为两个指针
{
int temp = *a; //取出a的值
*a = *b; // 把b的值给a
*b = temp; //把a放在temp里的值给b
}

#include <stdarg.h> //头文件支持
// 这个头文件 定义了一个类型和 三个宏 (暂且理解为函数)
// va_list
// va_start 准备访问 不确定部分的参数
// va_arg 逐个获得未确定部分
// va_end 结束


int Youradd(int a,...) //给出 多少个数的加法
{
int sum = 0; //求和
va_list var_arg; //定义一个va_list类型的变量
int count = 0; //定义个数
va_start(var_arg, a); //准备开始接收不确定的部分
//给出变量名 以及访问的参数个数
for (count = 0; count < a; count++)
{
sum += va_arg(var_arg, int);
//把不确定部分的参数用int的形式获取
// 然后加到sum上
}
va_end(var_arg); //结束
return sum;
}


十二:函数二
#include <stdio.h>

1. 函数指针 指针函数

指针函数: 返回值为指针的函数
不能返回临时变量的地址
函数指针: 指向函数的指针

函数指针的规则:
1.返回值类型 和 参数列表 共同决定了这个函数指针能指向的函数
只要返回值类型 和参数列表 和函数指针的一样 那么就可以被这个指针指向
2.(*p)
3.函数名前 (&) 可加可不加

函数指针的作用:
结构体 如果作为函数参数传递 的时候
需要拷贝整个结构体 浪费时间和空间 结构体指针作为参数传递
函数 如果作为函数参数传递 的时候

可以指向一系列 (返回值类型 参数列表)一致的函数

int add
int jian
int cheng
int chu

2.递归
迭代:循环 -- 从起点出发 逐步的接近终点 循环的判断条件
调整语句让循环变量逐步接近判断条件
保证不是死循环

递归:函数不断的调用自己 --- 从终点出发回到起点
递归的终点 就是结束递归的条件 (循环的判断条件)
不断的迫近递归终点

写一个递归
递归的终点一定要先写 ---- 不是死循环
然后要保证递归的变量在变--- 要有一个调整的过程

阶乘
5!=5*4*3*2*1;
5!=5*4!=5*4*3!;
n!=n*(n-1)!;
n=0;


迭代和递归的取舍:
递归的优势在于 提高代码的可读性 牺牲运行时的开销
迭代 减少开销 可读性不高

迭代是人 递归是神


char * Mystrcpy(char *des, char *res)
{
char *temp = des;

while (*temp++ = *res++)
;
return des;
}


int main()
{
char str1[20] = "helloworld!";
char str2[20];
char *s1 = str1;
char *s2 = str2;

void (*p2) ();
p2 = print;
p2();


char * (*p) (char *s1, char *s2);

return 0;
}
递归
int fac(int n)
{
if (n == 0) // 递归的终点 标志 0!=1
return 1;
else
return n*fac(n - 1);
}

int main()
{
//求5!
int n = 5;
int sum = 1;
for (int i = 1; i <= n; i++)
{
sum*=i;
}
int sum2 = fac(n);
printf("%d\t%d\n", sum, sum2);
return 0;
}
*/

//把一个整型的数 用递归求每一位

void Myadd(unsigned int value)
{
unsigned int temp;

temp = value / 10; //调整语句
if (temp != 0)//递归的终点
{
Myadd(temp);
}
putchar(value % 10 + '0');
//printf("%c", value % 10 + '0');
//输出一个字符
}

int main()
{
unsigned a = 23456;
Myadd(a);
printf("\n");

return 0;
}


char * Mystrcpy(char *des,char const *s2)
{
char *temp=des;
//定义一个临时指针 指向目标字符串
// 让这个临时指针来做拷贝操作 让目标字符串的首地址保持
//因为最后要返回目标字符串的首地址
// 在copy过程中 需要移动指针 导致目标字符串的首地址找不到了

//目标字符串

while ((*temp++ = *s2++) != '\0')
;
return des;
***不能在函数中返回临时变量的地址
}

//拷贝完之后 前面是烫 后面是内容
int main()
{
char str1[20] = "hello Seven"; //源字符串
char str2[20]; //目标字符串

char *s1 = str1;
char *s2 = str2;

s2=Mystrcpy(s2, s1);
puts(s2);
return 0;
}


十三:结构体

结构体

数组是 一堆具有相同数据类型的数据的集合

结构体 可以把有不同的数据类型的数据存放在一起
聚合数据类型 数组 结构体

结构体 把它的值 称为成员

struct tag
{
member list;
}variable-list;

struct ---关键字 结构体的关键字
tag 结构体的名字 ---- 可以缺省的
member list 结构体的成员列表
variable-list 声明的变量列表 ---可以缺省的

结构体 写在main外面 写在main里面都可以
只要保证声明在调用之前就可以

直接访问 间接访问:
. 直接访问 用于结构体变量访问结构体成员
-> 间接访问 用于结构体指针访问结构体成员

赋值 初始化 四种方法
第一 不能在结构体里赋值
第二 初始化方法
1.类似数组的初始化方法 ={};

2.枚举 逐个赋值
自引用

函数参数传递
函数传参的时候 拷贝 把实参拷贝一份给形参
如果 结构体 作为函数参数传递 拷贝的时候 会浪费很大的空间
倾向于 使用结构体指针作为函数参数传递
要看到时间差 结构要庞大一点

typedef
起别名

内存对齐
要求 结构体内定义变量的顺序
从大到小排列
从小到大排列

struct danny *summerseven; //自引用
// //不论是什么指针 大小都是4

//printf("%d\n", sizeof(struct danny)); //20 内存对齐
//内存对齐机制 对齐补齐
// 会以最大的 基本数据类型 为标准
// 4 10=4+4+2+1+1--->12 4*3 +4 =20


struct danny
{
unsigned int a : 3; //意味着 这个a只能占两位 11 10 01 00 0`3
//float d;
unsigned char b : 5;
//struct danny *summerseven; //自引用
//不论是什么指针 大小都是4
}ouguang;
//怎么写位段 直接在变量后写 :加上一个值
//这个值是 二进制的位数的意思
//一般地 写位段的时候 最好给出unsigned 或者 signed

ouguang.a = 8; // 101 01 保留有效位数

// int 占几个字节
// 不同的数据类型 会首先参照 对齐规则
printf("%d\n", sizeof(struct danny)); //4
//位段的作用 让结构体的内存 更紧凑
// 约束输入的数的范围

typedef struct danny
{
int a;
char c;
double d;
}laosiji,*summerseven;

typedef struct danny laosiji1;


十四:

解决问题:
指针 数组 函数 问题在哪里
问题描述出来哦 一个个讲解

1.函数数组结合 返回指针 然后输出?
2.这个问题没看明白 函数里是不是全是重要的东西? ---
3.为什么说指针不安全
程序关心的是 数据
指针 提供了另一种访问数据的方式
不确定 用指针去访问数据的时候 别人会不会做不安全的操作
提供const 保障一定的安全
4.指针解引用 是不是不带*

5.const和指针的作用

6.参数列表 如果是指针

7.两个指针能指同一个地方吗
int a = 5;
int *pa = &a; //二狗子
int *pb = &a; //大狗子
printf("%d\t%d\t%d\n", a, *pa, *pb);
*pa = 11;
//只对某一个指针修改操作 会改变其他的值
printf("%d\t%d\t%d\n", a, *pa, *pb);

8.什么是函数 ,函数的作用
把一系列具有某个功能的代码封装起来 方便以后调用
只需要简单的调用 就可以实现一样的功能 而不需要 重新写
每个功能写一个函数 提高可读性


9.字符数组,字符串和指针的结合 -- 这个问题描述的清楚一点
字符串是一个特殊的字符数组,
数组和指针
数组名在某些情况下是一个指向首地址的常量指针

10.函数内容和主函数衔接
第一个 没有返回值的函数
只有在函数内部 直接和主函数的东西发生关系
第二个 有返回值的函数
第一种 在函数内部 和主函数发生关系
第二种 通过返回值 修改一些东西

11.函数返回值,什么时候可以改变形参,什么时候用完就释放
局部变量 出了作用域就会释放
一对花括号 表示作用域 在一个作用域定义的局部变量
到另一个作用域内是不能使用的

12.结构体有数组 结构体有这个结构体 -- 这个其实昨晚结构体正课讲了
13.函数有没有规定的定义格式
函数的格式 就是想写什么就写什么
14.字符串数组的应用

十五:单链表1

#include <stdlib.h> //malloc需要头文件

单链表:
内存不连续,添加节点和删除节点很简单,查找会相对困难
中间添加 删除也可以

内存四区:
堆 栈 全局 代码
栈区内存会随着程序的死亡而释放
堆区内存必须手动申请,手动释放

链表的节点是由两部分组成的:
数据域 -- 真正关心的
指针域 -- 把链表连起来

增删改查
增加节点: 头插法 尾插法
查找节点: 必做
删除节点 选做
修改节点

动态内存分配:在堆区申请一块内存,需要用指针接住,因为这块内存没名字
malloc 在堆区申请一块内存 返回首地址的指针
返回的是 void * 如果 想要使用 必须强制转换类型

int a = 5; //空间在栈区
int *p = NULL;
p = (int*)malloc(sizeof(int));//在堆区申请一块空间
*p = 5;
printf("%d\t%d\n", a, *p);
free(p); //释放申请的堆区内存
//释放完这块空间之后 该指针变为野指针/没有指向空间
p = NULL; //因为不能确保p是否会被直接使用
//即使误操作 对p解引用 不会摊上大事情

int b[5] = { 1, 2, 3, 4, 5 };
int n;
scanf("%d", &n); //此时 这个数组就是一个动态的
int *pb = (int *)malloc(sizeof(int)* n); //申请空间
// int b[5];
//一次性申请的内存是连续的
for (int i = 0; i < 5; i++) //赋值
{
*(pb + i) = i + 5;
}
for (int i = 0; i < 5; i++) //打印
{
printf("%d\n", *(pb + i));
}
//这是一个良好的习惯
free(pb);
pb = NULL;

pa = (struct tag*)malloc(sizeof(struct tag));
//sizeof是多少 就会申请多大的堆区内存
pa->b = 'A';
printf("%c\n", pa->b);
free(pa);
pa = NULL;
int a = 5;
float b = 3.14f; //f 表示float 否则会隐式转换成都double
int *pa = &a;
float *pb = &b;
pa =(int *) pb;

struct tag //实际上 这就是链表的一个节点的结构体
{
int data;
struct tag *next; //自引用
}*pa;

pa = (struct tag*)malloc(sizeof(struct tag));
pa->data = 5;
//next指针也要开辟空间

free(pa);
pa = NULL;
// 先开辟的后释放
// 先申请pa 再申请next 要先释放next 再释放pa

/****************************/
#include <stdio.h>
#include <stdlib.h>
typedef struct link { /* 定义单链表结点类型 */
char data;
struct link *next;
}linklist;

linklist *CreateList_Front(); //头插法创建单链表
linklist *CreateList_End(); //尾插法创建单链表
linklist *CreateList_EndTwo(); //尾插法创建单链表(优化后 有头结点 少两次判断)
void ShowLinklist(linklist *h); //输出显示链表
void getdate(linklist *head, int pos); //查找pos位置的值
int modifylist(linklist *head, int pos); //修改pos位置的链表信息
int isEmptyList(linklist *head); //判断链表是否为空
void dellist(linklist **head, char ch); //删除链表某个节点

int main()
{
int choice;
linklist *head;
//head = (linklist*)malloc(sizeof(linklist));

while (1)
{
printf("单链表的创建\n");
printf("1.使用头插法创建单链表\n");
printf("2.使用尾插法创建单链表\n");
printf("3.链表输出显示\n");
printf("4.修改链表信息\n");
printf("5.删除链表某元素\n");
printf("8.退出\n");
printf("9.清除屏幕\n");
printf("做出选择:\n");
scanf("%d", &choice);
switch (choice)
{
//头插法
case 1:
head = CreateList_Front();
break;
//尾插法
case 2:
//head = CreateList_End();
head = CreateList_EndTwo();
break;
//输出链表
case 3:
ShowLinklist(head);
break;
//退出程序
case 4:
{
int pos;
printf("请输入要修改的位置:\n");
scanf("%d", &pos);
modifylist(head, pos);
break;
}
case 5:
{
//如果删除头结点 head=head->next;
char ch;
printf("请输入要删除的元素\n");
rewind(stdin);//清除缓冲
scanf("%c", &ch);
dellist(&head, ch);
//因为要修改头结点 所以要传入二级指针
break;
}
case 8:
return 0;
break;
case 9:
system("cls");
break;
default:
break;
}
}
getchar();
getchar();
return 0;
}

/*头插法 */
linklist * CreateList_Front()
{
linklist *head, *p;
char ch;
head = NULL;
printf("依次输入字符数据('#'表示输入结束):\n");
ch = getchar();
while (ch != '#')
{
p = (linklist*)malloc(sizeof(linklist));
p->data = ch;
p->next = head;
head = p;
ch = getchar();
}
return head;
}

/*不带头结点的尾插法*/
linklist *CreateList_End()
{
linklist * head, *p, *e;
//头结点 临时节点 尾节点
char ch;
head = NULL;
e = NULL;
printf("依次输入字符数据('#'表示输入结束):\n");
ch = getchar();
while (ch != '#')
{
p = (linklist *)malloc(sizeof(linklist));
p->data = ch;
if (head == NULL) //判断此时链表是否为空
//插入的数据是第一个数据也就是头
{
head = p;
}
else
{
e->next = p; //让尾节点的next指向p
}
//p->next 垃圾值
e = p; //保持 尾节点永远指向链表的尾部
ch = getchar();
}
if (e != NULL) //如果链表不为空,则最后一个节点的next为空
{
e->next = NULL;
}
return head;
}

/*带头结点的尾插法*/
linklist *CreateList_EndTwo()
{
linklist *head, *p, *e;
char ch;
head = (linklist *)malloc(sizeof(linklist));
//给head分配内存
//如果用这种尾插法创建链表
//要避开第一个头结点 不输出
// p=head->next;
e = head;
printf("依次输入字符数据('#'表示输入结束):\n");
ch = getchar();
while (ch != '#')
{
p = (linklist *)malloc(sizeof(linklist));
p->data = ch; //把数据放到链表节点的数据域
e->next = p; //让尾节点指向临时节点
e = p;//让临时节点变成尾节点
ch = getchar();
}
e->next = NULL;
return head;
//直接返回 head->next;
}

/*显示链表*/
void ShowLinklist(linklist * h)
{
linklist *p;
p = h;
if (p->next == NULL || p == NULL)
{
printf("链表为空\n");
return;
}


while (p != NULL)
{
printf("%c ", p->data);
p = p->next;
}
printf("\n");
}

/*修改链表某个位置的元素 ,修改成功返回0 否则返回1*/
//根据节点位置
int modifylist(linklist *head, int pos)
{
linklist *p = head; //这个地方 昨天的问题
int i = 0;
if (pos < 1)
{
printf("modifylist函数执行,pos值非法\n");
return 1;
}
if (head == NULL)
{
printf("modifylist函数执行,链表为空\n");
return 1;
}
while (p != NULL)
{
++i;
if (i == pos)
break;
p = p->next;//移到下一结点
}
if (i < pos) //链表长度不足则退出
{
printf("modifylist函数执行,pos值超出链表长度\n");
return 1;
}
else
{
char temp;
printf("请输入修改后的值:\n");
rewind(stdin);
scanf("%c", &temp);
p->data = temp;
return 0;
}

}

/*删除链表节点, 根据元素值*/
//根据节点元素
void dellist(linklist **head, char ch)
{
linklist *p = *head;
linklist *temp; //临时指针 方便操作
if (p->data == ch) //头结点 单独处理
{
*head = (*head)->next; //把表头指针往后移动
free(p); //删除了原来的表头的节点
}
p = (*head)->next; //把p指针 指向head之后
temp = *head;

while (NULL != p)//从第二个节点开始
{
//如果已知题目有两个或多个相同元素 想删除第几个
// flag 判断当前是第几个要删除的元素
if (p->data == ch) //找到元素 匹配到了要删除的元素
{
temp->next = p->next; //让第一个元素指向第三个
free(p);
break;
}
p = p->next; //指针指向下一个元素
temp = temp->next;
}

}

linklist * dellist1(linklist *head, char ch);
//可以让返回值为 结构体指针类型 在主函数用head接住返回值
//从而避免使用二级指针

十六:单链表2

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


1.链表删除元素
2.stdlib和stdio的区别
standard library
standard input output
3.结构体指针的自引用(即链表的结构体如何实现)
定义结构体的时候 必须确定其内存
可以放指针 可以放完整的结构体
可以放自己的指针 结构体的自引用
4.链表插入
就是找到要插入的位置,然后讲临时指针接上去,然后断开原来的链接
5.还挺好看的
6.是说直接打开cpp文件然后不能执行对嘛?
因为要有一些其他的内容支持
光有代码是不够的 光打开是不能运行的 没有 没有 没有
7.不是不教你们哦 是有些东西就是做不到
所有知识都会讲哦 课程体系内的
然后你们提出要求 有时间也会补课 可以补一些体系外的内容
8. 2.3开始讲项目 多关卡推箱子 成员管理系统
然后放假回来开始
函数2 函数3 枚举联合 预处理 文件 双向链表 栈 排序 队列


//typedef struct link linklist;
typedef struct link { /* 定义单链表结点类型 */
char data;
struct link *next; //指向一个结构体 因为指针就是4字节
//struct link x; //此时结构体还没定义完 内存大小不能确定
//inklist *x; 这是不允许的 因为取别名还在后面
}linklist;

linklist *CreateList_Front(); //头插法创建单链表
linklist *CreateList_End(); //尾插法创建单链表
linklist *CreateList_EndTwo(); //尾插法创建单链表(优化后 有头结点 少两次判断)
void ShowLinklist(linklist *h); //输出显示链表
void getdate(linklist *head, int pos); //查找pos位置的值
int modifylist(linklist *head, int pos); //修改pos位置的链表信息
int isEmptyList(linklist *head); //判断链表是否为空
void dellist(linklist **head, char ch); //删除链表某个节点

int main()
{
int choice;
linklist *head;
//head = (linklist*)malloc(sizeof(linklist));

while (1) //这是一个死循环
{
printf("单链表的创建\n");
printf("1.使用头插法创建单链表\n");
printf("2.使用尾插法创建单链表\n");
printf("3.链表输出显示\n");
printf("4.修改链表信息\n");
printf("5.删除链表信息\n");
printf("8.退出\n");
printf("9.清除屏幕\n");
printf("做出选择:\n");
scanf("%d", &choice);
switch (choice)
{
//头插法
case 1:
head = CreateList_Front();
break;
//尾插法
case 2:
//head = CreateList_End();
head = CreateList_EndTwo();
break;
//输出链表
case 3:
ShowLinklist(head);
break;
//退出程序
case 4:
{
int pos;
printf("请输入要修改的位置:\n");
scanf("%d", &pos);
modifylist(head, pos);
break;
}
case 5:
{
char ch;
printf("请输入要删除的元素:\n");
rewind(stdin); //要清空缓冲
scanf("%c", &ch);
dellist(&head, ch);
//head指针的地址其实就是二级指针
break;
}
case 8:
return 0;
break;
case 9:
system("cls");
break;
default:
break;
}
}
getchar();
getchar();
return 0;
}

/*头插法 */
linklist * CreateList_Front()
{
linklist *head, *p;
char ch;
head = NULL;
printf("依次输入字符数据('#'表示输入结束):\n");
ch = getchar();
while (ch != '#')
{
p = (linklist*)malloc(sizeof(linklist));
p->data = ch;
p->next = head;
head = p;
ch = getchar();
}
return head;
}

/*尾插法1(无头结点)*/
linklist * CreateList_End()
{
linklist *head, *p, *e;
char ch;

head = NULL;
e = NULL;
printf("请依次输入字符数据('#'表示输入结束):\n");
ch = getchar();
while (ch != '#')
{
p = (linklist*)malloc(sizeof(linklist));
p->data = ch;
if (head == NULL) //先判断输入的是不是第一个节点
head = p;
else
e->next = p;
e = p; //e始终指向输入的最后一个节点
ch = getchar();
}
if (e != NULL) //如果链表不为空,则最后节点的下一个节点为空
e->next = NULL;
return head;
}

/*尾插法2(有头结点)*/
linklist *CreateList_EndTwo()
{
linklist *head, *p, *e;
char ch;

head = (linklist*)malloc(sizeof(linklist));
e = head; //让e指向头节点
ch = getchar();
while (ch != '#')
{
p = (linklist*)malloc(sizeof(linklist));
p->data = ch;
e->next = p; //把新节点添加到表尾
e = p; //把指针指向新节点
ch = getchar();
}
e->next = NULL; //尾节点的指针域置空
return head;
}

/*显示链表*/
void ShowLinklist(linklist * h)
{
linklist *p;

p = h;
while (p != NULL)
{
printf("%c ", p->data);
p = p->next;
}
printf("\n");
}

/*查找链表某位置的元素 */
void getdate(linklist *head, int pos)
{
linklist *p = head;
int i = 0;
if (pos < 1)
{
printf("getdate函数执行,pos值非法\n");
return;
}
if (head == NULL)
{
printf("getdate函数执行,链表为空\n");
return;
}
while (p != NULL)
{
++i;
if (i == pos)
break;
p = p->next;//移到下一结点
}
if (i < pos) //链表长度不足则退出
{
printf("modifylist函数执行,pos值超出链表长度\n");
return;
}
else
{
printf("modifylist函数执行,pos值为%c:\n", p->data);
}
}

/*修改链表某个位置的元素 ,修改成功返回0 否则返回1*/
int modifylist(linklist *head, int pos)
{
linklist *p = head;
int i = 0;
if (pos < 1)
{
printf("modifylist函数执行,pos值非法\n");
return 1;
}
if (head == NULL)
{
printf("modifylist函数执行,链表为空\n");
return 1;
}
while (p != NULL)
{
++i;
if (i == pos)
break;
p = p->next;//移到下一结点
}
if (i < pos) //链表长度不足则退出
{
printf("modifylist函数执行,pos值超出链表长度\n");
return 1;
}
else
{
char temp;
printf("请输入修改后的值:\n");
rewind(stdin);
scanf("%c", &temp);
p->data = temp;
return 0;
}

}

//检查单链表是否为空,若为空则返回1,否则返回0
int isEmptyList(linklist *head)
{
if (head == NULL)
{
printf("isEmptyList函数执行,链表为空\n");
return 1;
}
printf("isEmptyList函数执行,链表非空\n");

return 0;
}

/*找到要删除的元素 然后执行删除操作*/
// 涉及到要删除头结点
// 匹配到怎么删 头节点要单独讨论 要传入二级指针
void dellist(linklist **head, char ch)
{
linklist * p = *head;
linklist * temp;
if (p->data == ch) //匹配到头结点的值是要删除的
{
*head = (*head)->next; //让头结点指向下一个位置
free(p); //释放头结点
}
p = (*head)->next;
//p = p->next; 如果进入了if分支 此时p已经被释放
temp = *head; //head在p前面
//不要一上来就开始写代码 先思考一下 怎么写
while (NULL != p)
{
if (p->data == ch)
{
temp->next = p->next;// 图
free(p);
break; //不能保证 整个链表 只有一个匹配值
}
//if如果没有匹配到 那么就要继续匹配下一个节点
p = p->next;
temp = temp->next;
}
//如果不使用二级指针 return head;
}

#endif
//程序执行的时间
#include <time.h>
int main()
{
tstart = clock(); //获取当前的时间
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= i; j++)
{
printf("%d * %d = %d ", j, i, i*j);
}
printf("\n");
}
tstop = clock();
printf("%d(ms)\n", tstop - tstart);
return 0;
}

 

十七:预处理(联合枚举)

枚举就是穷举 把所有可能列出来
放在枚举内的 称为 标识符 实际上 是一个整型
同一个标识符 只能属于一个枚举
规则
1.只能放标识符 不能直接放整型
2.同一个标识符 只能被一个枚举引用
3.默认的 第一个枚举的成员会从0开始 其后的每一个逐个+1
4.可以给任意一个赋值

enum Weekday //5以内的整数 的枚举
{
one,two,three=0,four,five // 0 1 0 1 2
};

enum dir
{
LEFT,RIGHT,UP,DOWN
}x;


联合 (共用)
大致上和结构类似
区别: 内存覆盖
任何一个时刻 联合体都只有一个值
但是可以在整个时间段内访问所有的成员
会按照你想要的方式展示出来

永远只会表现一个值 共用一个内存

规则:
结构体内存对齐规则 如果超过的 会朝4对齐 char 3--->4
1.关于内存大小
先找到整个联合体内 内存最大的单位 然后对齐最大的基本数据类型
如果超过4 但是不是4的整数倍 就会对齐 朝着比它大的最小的整数倍靠近
2.对联合体的初始化 只能初始化第一个成员


enum dir
{
a,b,c,d,e,f,g,h,i,j,k//0 1 2......
};

union xx
{
int a;
char b;
}m = {49};

int main()
{
printf("%d\n", m.a);
printf("%c\n", m.b);
return 0;
}
*/

struct danny
{
int ball;
char name[5];
};

union Ouguang
{
//char name[9];//xia
int a;
//int ball; //5
float f;
//char d;
}x = { 0 };


预处理:

宏定义
#define name stuff
只要在程序中遇到name就会替换成stuff

#define机制包括了一个规定,允许把参数替换到文本中

一般的 习惯上会大写宏名

如果宏定义过长 可以用 \ 来换行(一般会人为的把宏名和内容分成两行)


"#value" 会输出 value

副作用:
如果参数带++ -- 那么可能会多次执行 从而导致结果不可预测
如果宏中带了 getchar();

#undef 用来取消某个宏
作用:
1.不想用某个宏
2.要改变某个宏

宏函数
相较于函数的优点:
1.无视类型
2.更快速

相较于函数的缺点:
1.代码膨胀

如何区别宏函数和函数
一般的 函数会小写或者遵循标识符规则
宏函数会全部大写

条件编译
#if constant-expression
statements
#endif
#if 其后 可以跟表达式 常量
条件编译 可以嵌套

文件包含
#include
<> 引入库文件
"" 引入自己写的文件

***头文件的重复包含问题***
因为多文件操作的时候 无法保证一个头文件只被一个文件包含
会发生头文件重复包含
解决方案:
1. #pragma once 放在头文件最前面 用来保证头文件只包含一次
这个命令 随编译器的变化而变化
2.
#ifndef __A_H__ // 判断这个宏有没有定义
#define __A_H__
#endif
if(__A_H__==0)
如果这个宏没有定义过 就是0 那么 if表达式 为真
执行其后的语句
如果这个宏定义过 就是1 那么 if表达式为假
跳过其后的语句 直接执行 endif

头文件 不要包括 函数的定义 和 变量的定义
*/

 


#define Print(format,value) \
printf("the value is "format" \n", value)

#define Print2(format,value) \
printf("the value of "#value" is "format" \n", value)

#define MAX(a,b) (a>b?a:b)

#define ONEBALL 0
#undef ONEBALL
#define ONEBALL 8

#include "a.h"
#include "b.h"


十八:文件操作1

文件

文件结构体指针 FILE

关于文件操作的函数

打开文件
fopen(const char *path,const char *mode)
打开文件 第一个参数给出文件名,打开方式
第一个参数默认同路径,如果要给别的路径 写相对路径 绝对路径
关闭文件
fclose(FILE *fp);
关闭文件 给出打开的文件指针即可

写入字符
fputc(char c,FILE* fp);
要写入的字符, 文件指针
一次写多个字符 只会保留最后一个
多次写多个字符 都会保留
读出字符
fgetc(FILE *fp);
如果读到文件结尾或者出错 返回EOF (一般EOF为-1)

写入字符串
fputs(char *str,FILE *fp);
写入的字符串,文件指针

数组名 在大部分情况下可以当做指针常量使用

读出字符串
fgets(char *str,int size,FILE *FP);
从文件中获取字符串
目标字符串的数组名或者指针,读取的长度,文件指针
实际上读到的长度是 size -1 因为字符串存在默认结尾\0

写入和读取
写入文件
fread fwrite
人为的称为乱码 实际上 只是正常的

fscanf fprintf


scanf 从键盘获得 进入缓冲区 直接从缓冲区获得数据
数据 流

int main()
{

FILE *fp = fopen("108.txt", "w");

w 是write 只写 如果没有这个文件 会创造一个 CPP同路径下
如果有 打开 然后写入内容 如果文件本身有内容会被清空
r read 只读 必须要有文件
a append 以写方式打开 追加在原文件之后 不会覆盖原来的内容

b 二进制文件
t 文本文件
+ 可读写
w+ wb+ wt+ rb rb+ rt+

//fputc('12', fp); // 2
fputc('1', fp);
fputc('2', fp);
fclose(fp);

//FILE *fp1 = fopen("108.txt", "w");
//fputc('M', fp1);
//fclose(fp1);

FILE *fp1 = fopen("108.txt", "r");
// 如果读多个 读到一个就停止
char ch;
ch = fgetc(fp1);
printf("%c\n", ch);
fclose(fp1);

FILE *fp = fopen("71.txt", "w");
fputs("XiaQi",fp);
fputs("OuGuang", fp);
fclose(fp);

FILE *fp1 = fopen("71.txt", "r");
char ch[20] = { 0 };
//fgets(ch, 5, fp1); //因为存在结尾\0 所以只能读取4个
//printf("%s\n", ch);
fgets(ch, 1024, fp1);//可以给出一个较大值 直接暴力
printf("%s\n", ch);
fclose(fp1);

FILE *fp = fopen("95.txt", "w");
while (1)
{
scanf("%s%d%d", mystudent.name, &mystudent.age, &mystudent.num);
fwrite(&mystudent, sizeof(struct STU), 1, fp);
//要写入的内容的首地址,写入内容的大小,个数,文件指针
printf("是否继续输入?(Y/N)\n");
fflush(stdin); // 清空缓冲
int ch = getchar();
if (ch == 'n' || ch == 'N')
{
break;
}
}

fclose(fp);

FILE *fp1 = fopen("95.txt", "r");
fread(&mystudent, sizeof(struct STU), 1, fp);
//要读出的内容的首地址,写入内容的大小,个数,文件指针
printf("%s\t%d\t%d", mystudent.name, mystudent.age, mystudent.num);
fclose(fp1);


FILE *fp = fopen("147.txt", "w");
scanf("%s%d%d", mystudent.name, &mystudent.age, &mystudent.num);
fprintf(fp, "%s\t%d\t%d\t", mystudent.name, mystudent.age, mystudent.num);
// 文件指针,分别描述其后的参数的占位符 ,逐个元素
//比printf多了第一个参数 文件指针
fclose(fp);

FILE *fp1 = fopen("147.txt", "r");
fscanf(fp1, "%s%d%d", mystudent.name, &mystudent.age, &mystudent.num);
//不是简单的叫文件指针 实际上是流
// 输入流 输出流
// fflush(stdin) --- 清空输入流
// stdout -- 输出流
fprintf(stdout, "%s\t%d\t%d\t", mystudent.name, mystudent.age, mystudent.num);
printf("\n----------------------\n");
printf("%s\t%d\t%d\t", mystudent.name, mystudent.age, mystudent.num);
fclose(fp1);

return 0;
}

 


定位 默认打开文件会在文件的首

fseek(FILE *stream,long offset,int origin);
流 偏移量 起始地址
偏移量:
正-- 正向偏移 实际上就是向后
负-- 负向偏移 实际上就是向前
因为规定形式 是 long 所以要在数字后+L
按照给出的偏移量偏移 以字节为单位
nL --- n个字节
起始位置:
SEEK_SET 文件开始位置 0
SEEK_CUR 文件当前位置 1
SEEK_END 文件结尾位置 2

rewind
把位置指针 重新指向流(数据流/文件)的开头
rewind(stdin);

 


int main()
{
FILE *fp = fopen("98.txt", "w");
fputs("qwertyuiopasdfghjklzxcvbnm", fp);
fclose(fp);

FILE *fp1 = fopen("98.txt", "r");
fseek(fp1, 20L, SEEK_SET);
char str[10];
fgets(str, 6, fp1);
printf("%s\n", str);
fclose(fp1);

return 0;
}

十九:文件操作2

《文件操作》
文本文件:

1.文本文件也称为ASCII文件.

2.这种文件在磁盘里面存放时每一个字符对应一个字节,用于存储对应的ASCII码.

二进制文件:

1.这种文件时按二进制编码进行存储的.

fopen(要打开的文件,打开方式);

打开方式:

w 只写,文件存在则清空,不存在则创建. (w) write
r 只读,文件必须存在 (r) read
r+ 读写,文件必须存在
w+ 读写,文件存在则清空,不存在则创建.
rb
wb

关闭文件:

int fclose( FILE * _File);


参数是一个文件指针,就是你要关闭的文件指针.

fgetc()和fputc()

int fputc( int _Ch, FILE * _File);

参数1: 要写入的字符 参数二:要写入的文件.

int fgetc( FILE * _File);

函数的作用:从文件里面读取一个字符

参数: 要读取的文件指针.

fwrite()和fread()

size_t fwrite( void * _Str, size_t Size, size_t Count, FILE * File);

函数的作用: 往文件里面写入一个字符串.

参数1: 要写的内容 参数2:写入的大小 参数3:写入的次数 参数4:要写入的文件

size_t fread(void * _DstBuf, size_t ElementSize, size_t _Count, FILE * _File);

函数的作用: 从文件里读取相应字符.

参数1:保存读取的字符 参数2:要读取大小 参数3:要读取的次数 参数4:要读取的文件

fprintf()和fscanf()

int fprintf( FILE * _File, const char * _Format, ...);

函数的作用: 格式化写入文件.

int fscanf(FILE * _File, const char * _Format, ...);

函数的作用: 格式化读取文件.

int fseek( FILE * _File, long _Offset, int _Origin);

函数作用:设置文件指针的偏移量

参数1:要设置的文件 参数2: 偏移量 参数3:参照位置

参照位置:
SEEK_SET 文件头部
SEEK_CUR 文件指针当前位置
SEEK_END 文件指针尾部

long ftell( FILE * _File);

函数作用:获取文件指针的位置

参数:要获取文件指针位置的文件。

二十:排序一
#include <stdio.h>

简单排序
默认从小到大排列

交换排序:冒泡排序 快速排序
插入排序:直接插入排序 二分插入排序 希尔插入排序
选择排序:选择排序 堆排序
冒泡排序
快速排序
选择排序
插入排序

C语言实现的最基础的4个排序算法:冒泡 快排 插入 选择

稳定性:
如果数组中存在两个相同的元素 如果排序后仍然按照原来的位置排序
那么我们称这个排序方法是稳定的

稳定的排序 : 直接插入 冒泡 归并 基数(桶)
不稳定的排序 : 希尔 直接选择 堆 快排

时间复杂度: O n n平方 n立方 n是数据规模 数组多大 待排序的数有多少个
一般的: 两层循环 n平方 三层循环 n立方
三个:
平均情况
最优

空间复杂度:

C语言可以实现8个:

如果想优化一个算法 :
1.推倒 重新写 ----飞跃 失败

2.剪枝--- if for
减少判断的次数 减少跳转的次数 减少算法的复杂度

 

void Bubblesort(int a[], int n)
{
for (int i = 0; i < n - 1; i++) //表示比较的轮数
{
for (int j = 0; j < n - i - 1; j++) //表示比较的次数
{
if (a[j]>a[j + 1])
{
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}

//优化一:添加一个标志 用来判断当前数是否已经排序过
//如果是 就跳过
void Bubblesort2(int a[], int n) //优化冒泡
{
int low = 0;
int high = n - 1;
int temp, j;
while (low < high)
{
for (j = low; j < high; j++)
{
if (a[j]>a[j + 1])
{
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
high--;
for (j = high; j > low; j--)
{
if (a[j]<a[j - 1])
{
temp = a[j];
a[j] = a[j - 1];
a[j - 1] = temp;
}
}
low++;
}
}

/*
外层循环:两个内循环 一个做右边 一个做左边
外层循环结束的时候 : 一轮 左边比标志小 FLAG 右边比标志大
进入递归

快排的优化 :
因为取到第一个数 可能会降低效率
随机取其中的任意一个数 作为flag

*/
void quicksort(int a[], int low, int high)
{
int i, j, flag, m;
if (low < high)
{
i = low;
j = high;
flag = a[low]; //取第一个数作为标志数
while (i < j) //外层循环
{
while (i<j && a[j]>flag)
j--;
//假设 在右边找到一个比标志小的数
if (i < j) //将小于标志的数放到a[i]上
{
m = a[i];
a[i] = a[j];
a[j] = m;
i++;
}

while (i < j && a[i] < flag)
i++;
//假设 在左边找到一个比标志大的数
if (i < j)
{
m = a[j];
a[j] = a[i];
a[i] = m;
j--;
}
}

a[i] = flag; //最后空的位置就是flag的位置
quicksort(a, low, i - 1); //左边做快速排序
quicksort(a, i + 1, high); //右边做快速排序
}

}

// 二分选择排序 找到两个 最大和最小
void selectsort(int a[], int n)
{
for (int i = 0; i < n - 1; i++) //从第一个位置开始比较
{
int k = i; // 用k标记当前位置为最小的位置
for (int j = i + 1; j < n; j++)
{
if (a[j] < a[k]) //遍历从i开始之后的整个数组 找到真正最小的
k = j;
}
if (k != i)
{
int temp = a[i];
a[i] = a[k];
a[k] = temp;
}
}
}

void insertsort(int a[], int n)
{
for (int i = 1; i < n; i++) //因为 默认第一个数有序
{
if (a[i] < a[i - 1]) //在遍历数组的过程中 待排序位置比原有的小
{
int j = i - 1;
int temp = a[i]; // 作为 哨兵
a[i] = a[i - 1]; //这一步可以放到循环内
while (j >= 0 && temp < a[j])//给待排序元素 腾位置
{
a[j + 1] = a[j];
j--;
}
a[j + 1] = temp;
}
}
}


int main()
{
int a[10] = { 123, 45, 67, 78, 4, 34, 56, 76, 34, 10 };
printf("before sort:\n");
for (int i = 0; i < 10; i++)
printf("%d\t", a[i]);
printf("\n");
//Bubblesort(a, 10);
//Bubblesort2(a, 10);
//quicksort(a, 0, 9);
selectsort(a, 10);
printf("after sort:\n");
for (int i = 0; i < 10; i++)
printf("%d\t", a[i]);
printf("\n");
return 0;
}


寻找质数的优化:
6 * n - 1 6 * n 6 * n + 1 6 * n + 2 6 * n + 3 6 * n + 4 6 * n + 5 6 * (n + 1)

6 *n +
2 = 2(3 * n + 1)6 *n + 3 = 3(2 * n + 1)6 *n + 4 = 2(3 * n + 2)6 *n + 5 =

二十一:排序二
八大排序代码汇总:
#include<iostream>

using namespace std;
#define N 10000
void Print(int a[], const int len); //输出函数
void Sort1(int a[], const int len); //方法一:冒泡
void Sort2(int a[], const int len); //方法二:异或(只能整型)
void Sort3(int a[], const int len); //方法三:插入排序
void Sort4(int a[], const int len); //方法四:选择排序
void Sort5(int a[], const int len); //方法五:希尔排序
void Sort6(int a[], int begin, int end); //方法六:快速排序
void Sort7(int a[], const int len); //方法七:堆排序
void Sort8(int a[], const int len); //方法八:归并排序

int main()
{
int a[N], len, NUM;
cout << "请输入你要使用的排序方:\n1:冒泡排序\n2:异或排序\n3:插入排序\n4:选择排序\n5:希尔排序\n6:快速排序\n7:堆排序\n8:归并排序" << endl;
cin >> NUM;
cout << "请输入排序个数:" << endl;
while (cin >> len) //多组输入
{
cout << "请输入这" << len << "个数:" << endl;
for (int i = 0; i < len; i++)
cin >> a[i];
if (NUM == 1)
if (NUM == 2)
Sort2(a, len);
if (NUM == 3)
Sort3(a, len);
if (NUM == 4)
Sort4(a, len);
if (NUM == 5)
Sort5(a, len);
if (NUM == 6)
Sort6(a, 0, len - 1);
if (NUM == 7)
Sort7(a, len);
if (NUM == 8)
Sort8(a, len);
Print(a, len);
cout << "想继续排序吗?想输入Y/y,不想输入N/n:" << endl;
char Y;
cin >> Y;
if (Y == 'N' || Y == 'n')
break;
cout << "请输入你要使用的排序方:\n1:冒泡排序\n2:异或排序\n3:插入排序\n4:选择排序\n5:希尔排序\n6:快速排序\n7:堆排序\n8:归并排序" << endl;
cin >> NUM;
cout << "请输入这" << len << "个数:" << endl;
}
return 0;
}

void Print(int a[], const int len)
{
cout << "用此方法排序后:" << endl;
for (int i = 0; i < len; i++)
{
if (i == 0)
cout << a[i];
else
cout << " " << a[i];
}
cout << "\n"
<< endl;
}

void Sort1(int a[], const int len) //方法一:冒泡
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (a[j] > a[j + 1]) //每次把体格最大数沉底
{
int t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
}

void Sort2(int a[], const int len) //方法二:异或,缺点:只能整型
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (a[j] > a[j + 1]) //其实和冒泡差不多
{
a[j] ^= a[j + 1];
a[j + 1] ^= a[j];
a[j] ^= a[j + 1];
}
}
}
}

void Sort3(int a[], const int len) //方法三:插入排序
{
for (int i = 1; i < len; i++) //从第二个开始
{
for (int j = 0; j < i; j++) //从第一个到i-1
{
if (a[j] > a[i])
{
int t = a[i]; //先保存最后一个数
for (int k = i; k > j; k--)
{
a[k] = a[k - 1]; //从k-1依次向后移动
}
a[j] = t; //保存的值放到j;
break; //插入之后退出
}
}
}
}

void Sort4(int a[], const int len) //方法四:选择排序
{
for (int i = 0; i < len - 1; i++) //选择最小的出来
{
int k = i;
for (int j = i + 1; j < len; j++)
{
if (a[j] < a[k])
{
k = j; //保存当前最小的位置
}
}
int t = a[i];
a[i] = a[k];
a[k] = t;
}
}

void Sort5(int a[], const int len) //方法五:希尔排序
{
//先分n组,组内排序,排序之后再分n/2组,排序。n/4...n/8......1
int d = len / 2; //分成d组
//a[0] a[0+d] a[0+2d] a[0+3d]
//a[1] a[1+d] a[1+2d] a[1+3d]
//...
while (d != 0)
{
for (int i = 0; i < d; i++) //排序d组
{
//第i组 a[i+0] a[i+d] a[i+2d] a[i+3d]
for (int j = i; j < len; j += d) //里面组内可以用其他排序
{ //这里一选择排序为例子
int k = j;
for (int m = j + d; m < len; m += d)
{
if (a[m] < a[k]) //找最小元素
{
k = m;
}
}
int t = a[j]; //交换
a[j] = a[k];
a[k] = t;
}
}
d /= 2;
}
}

void Sort6(int a[], int belenin, int end) //方法六:快速排序
{
int Part(int a[], int belenin, int end); //声明排序函数
//数组分为两部分,放左右两边,分别对两部分进行排序
//然后两部分里面再分为两个子部分,依此类推,像递归一样
if (belenin >= end)
return; //不需要排序
int k = Part(a, belenin, end);
Sort6(a, belenin, k - 1);
Sort6(a, k + 1, end);
}

int Part(int a[], int belenin, int end) //方法六的一个调用排序函数
{
int k = belenin; //选择开头
//a[belenin]放到a[i]和 a[j]之间
//左边是i,右边是j
//从左往右 找一个比a[belenin]小的数字
//从右往左 找一个比a[belenin]大的数字
int i = belenin, j = end;
while (i < j) //当i==j的时候 i就是a[belenin]应该在的位置
{
while (i < j && a[j] >= a[belenin])
j--; //a[j]右边数字都要大于等于a[belenin]
while (i < j && a[i] <= a[belenin])
i++; //a[i]右边数字都要小于等于a[belenin]
int t = a[i]; //交换a[i]和a[j]
a[i] = a[j];
a[j] = t;
}
//交换a[belenin]和a[i]
int t = a[belenin];
a[belenin] = a[i];
a[i] = t;
return i;
}

/*

堆的结构类似于完全二叉树,每个结点的值都小于或者等于其左右孩子结
点的值,或者每个节点的值都大于或等于其左右孩子的值

堆排序过程将待排序的序列构造成一个堆,选出堆中最大的移走,再把剩
余的元素调整成堆,找出最大的再移走,重复直至有序


来看一下实现*/

//堆排序
void Sort7(int a[], const int len)
{
int i;
void Heapify(int a[], const int first, int end); //声明排序函数
//初始化堆,从最后一个父节点开始
for (i = len / 2 - 1; i >= 0; --i)
{
Heapify(a, i, len);
}
//从堆中的取出最大的元素再调整堆
for (i = len - 1; i > 0; --i)
{
int temp = a[i];
a[i] = a[0];
a[0] = temp;
//调整成堆
Heapify(a, 0, i);
}
}

void Heapify(int a[], const int first, int end) //声明方法七中的一部分
{
int father = first;
int son = father * 2 + 1;
while (son < end)
{
if (son + 1 < end && a[son] < a[son + 1])
++son;
//如果父节点大于子节点则表示调整完毕
if (a[father] > a[son])
break;
else
{
//不然就交换父节点和子节点的元素
int temp = a[father];
a[father] = a[son];
a[son] = temp;
//父和子节点变成下一个要比较的位置
father = son;
son = 2 * father + 1;
}
}
}

void Sort8(int a[], const int len)
{
void Merlene(int a[], int relen[], int start, int end); //声明方法八的一部分
//创建一个同样长度的序列,用于临时存放
int reg[len];
Merlene(a, reg, 0, len - 1);
}

void Merlene(int a[], int relen[], int start, int end)
{
if (start >= end)
return;
int len = end - start, mid = (len >> 1) + start;

//分成两部分
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
//然后合并
Merlene(a, relen, start1, end1);
Merlene(a, relen, start2, end2);

int k = start;
//两个序列一一比较,哪的序列的元素小就放进relen序列里面,然后位置+1再与另一个序列原来位置的元素比较
//如此反复,可以把两个有序的序列合并成一个有序的序列
while (start1 <= end1 && start2 <= end2)
relen[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];

//然后这里是分情况,如果a2序列的已经全部都放进relen序列了然后跳出了循环
//那就表示a序列还有更大的元素(一个或多个)没有放进relen序列,所以这一步就是接着放
while (start1 <= end1)
relen[k++] = a[start1++];

//这一步和上面一样
while (start2 <= end2)
relen[k++] = a[start2++];
//把已经有序的relen序列放回a序列中
for (k = start; k <= end; k++)
a[k] = relen[k];
}
二十二:栈
#include <stdio.h>

栈:
栈的数据特点:先进后出 后进先出 没有具体的规则 只要符合特点就是栈
特点决定用法: 只要符合栈的规则的地方 都可以使用栈
printf

栈顶:栈最上面的元素
栈底:栈最底下的元素
两(三)个常用的说法:
push(压栈):把数据放入栈
pop(弹出):把栈顶的数据从栈 拿出
top:返回栈顶的数据 但是 不取出
判断栈是否为空:就不能pop top
判断栈是否满:就不能push
栈区别于队列: 栈永远只能操作栈顶

静态数组实现栈
缺陷:
弹出操作 并没有真的释放空间

优势:
不需要人为的写连接关系

链表实现栈

断言:
#include <assert.h> //断言

类似 if 用来判断情况
assert(expression);
如果为真 则继续执行
如果为假 则程序直接结束

debug下 断言可以用来找bug 发生错误 直接中止程序
release下 只需要定义一个NDEBUG的宏 就会自动跳过所有有关断言的语句

缺陷:
1. 占用时间
2. 如果涉及到 ++ -- 类似的 修改变量的操作 不能用断言

 

//用静态数组实现栈
#define STACK_TYPE int //栈内元素的数据类型
#define STACK_SIZE 100 //栈的大小
STACK_TYPE stack[STACK_SIZE]; //用数组模拟栈
int top_element = -1; //标志栈顶的位置
//栈顶元素 最开始的时候 栈为空 假定 -1就是空的标志

void push(STACK_TYPE value);//压栈
void pop(void); //弹出
STACK_TYPE top(void); //返回栈顶元素
int EEE(void); //判断栈是否为空
int is_full(void); //判断栈是否满

void push(STACK_TYPE value)
{
//if (!is_full())//判断是否栈满 if()
assert(!is_full());
top_element += 1; //元素+1
stack[top_element] = value;
}

void pop(void)
{
assert(!EEE());//判断是否为空
printf("现在的栈顶的值为=%d\n", stack[top_element]);
top_element -= 1; //让栈的有效个数-1 实际上这个值并没有舍弃
}

STACK_TYPE top(void)
{
assert(!EEE());//判断是否为空
return stack[top_element];
}

int EEE()
{
return top_element == -1; //只需要判断栈顶的位置 是不是-1
//如果栈为空 则 返回 1
// 如果栈不为空 返回 0
}

int is_full()
{
return top_element == STACK_SIZE - 1;
}

int main()
{
push(161);
push(9);
push(87);
push(138);
pop();
printf("调用top返回的栈顶元素=%d\n",top());
pop();
printf("%d\n", stack[3]);
return 0;
}


//用链表实现栈
#include <stdlib.h>
#define STACK_TYPE int //栈内元素的数据类型
#define STACK_SIZE 100 //栈的大小

typedef struct STACK_NODE
{
STACK_TYPE value;
STACK_NODE * next;
}StackNode;

StackNode * stack;

void create_stack(int size); //创建栈
void destroy_stack(void);//销毁栈
void push(STACK_TYPE value);//压栈
void pop(void); //弹出
STACK_TYPE top(void); //返回栈顶元素
int EEE(void); //判断栈是否为空
int is_full(void); //判断栈是否满

void create_stack(int size)
{
// 为了科学 献身 写一个函数
}

void destroy_stack(void)
{
while (!EEE())
{
pop();
}
}

void push(STACK_TYPE value) //实际上是头插法
{
StackNode * new_node;
new_node = (StackNode *)malloc(sizeof(StackNode));
assert(new_node != NULL);// 判断是否开辟空间成功
new_node->value = value; //赋值
new_node->next = stack; //新节点指向原来的栈顶
stack = new_node; // 要让stack永远指向栈顶
}

void pop(void)
{
StackNode * temp_node;
assert(!EEE());
temp_node = stack; //类似链表操作 不需要申请空间
printf("pop函数调用的栈顶=%d\n", temp_node->value);
stack = temp_node->next; // 栈顶下移一位
free(temp_node); //释放原来的栈顶
}

STACK_TYPE top()
{
assert(!EEE());
return stack->value;
}

int EEE(void)
{
return stack == NULL;
}

int is_full(void)
{
// 为了科学! 献身! 写一个函数
return 0;
//直接return 0 意味着 永远不会满
//链表 新节点进来就开辟空间
}

int main()
{
push(119);
push(147);
push(87);
push(135);
push(23);
printf("top函数调用栈顶=%d\n", top());
pop();
printf("top函数调用栈顶=%d\n", top());
printf("\n--------------\n");
destroy_stack();
if (EEE())
printf("链表为空\n");
return 0;
}

二十三:队列

队列 : 先进先出 后进后出
队首 队尾
出队操作 发生在 队首
入队操作 发生在 队尾

力学第X定律 : 能量是守恒的
人脑考虑的量+电脑运算的量=定值
写一个项目-->debug-->优化

静态数组 模拟 循环队列
人为的把front往后移动一位 用数组的访问方式去访问 这个数据仍然是存在


单链表 模拟 队列

双向链表 因为有两个指针域 一个指向前面 一个指向后面
只要给出其中任意一个节点 就可以遍历整个链表

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


// 可以不加1 自行尝试 什么后果
// 静态数组 模拟 循环队列 动态数组
#define QUEUE_SIZE 100 //定义队列大小
#define ARRAY_SIZE (QUEUE_SIZE+1) //定义数组大小
#define QUEUE_TYPE int
//静态数组实现队列
QUEUE_TYPE queue[ARRAY_SIZE];
static int front = 1;//队首 实际上 要加上static
int rear = 0;//队尾

void Insert(QUEUE_TYPE value); //入队
void Delete(void);//出队
QUEUE_TYPE Front(void);//返回队首
int EEE(void);// 判断队列是否为空
int is_full(void); //判断队列是否满

void Insert(QUEUE_TYPE value)
{
assert(!is_full());
rear = (rear + 1) % ARRAY_SIZE;
queue[rear] = value;
}

void Delete(void)
{
assert(!EEE());
printf("%d出队\n", queue[front]);
queue[front] = 0;
front = (front + 1) % ARRAY_SIZE;
}

QUEUE_TYPE Front(void) //回到队首
{
assert(!EEE());
return queue[front];
}

int EEE(void)
{
return (rear + 1) % ARRAY_SIZE == front; //判断队列是否为空
}

int is_full(void)
{
return (rear + 2) % ARRAY_SIZE == front;
}

int main()
{
Insert(3);
Insert(4);
Insert(5);
Insert(6);
printf("此时的队首%d\n", Front());
Delete();
printf("此时的队首%d\n", Front());
printf("%d\n", queue[1]);
return 0;
}

//链表实现队列
typedef int Queuetype;

typedef struct Node{ // 节点的结构体
Queuetype element;
struct Node *next;
}NODE, *PNODE;

typedef struct QNode{ // 队列
PNODE Front, Rear;
}Queue,*PQueue;

void InitQueue(PQueue queue); //创建队列函数
int isEmptyQueue(PQueue queue); //判断是否为空
//int isFullQueue(PQueue queue); //判断是否满
void InsertQueue(PQueue queue, int val); //入队
void DeleteQueue(PQueue queue); //出队
void DestroyQueue(PQueue queue); //销毁
void bianliQueue(PQueue queue); //遍历队列


void InitQueue(PQueue queue)
{
queue->Front = queue->Rear = (PNODE)malloc(sizeof(NODE));
assert(queue->Front);
//assert(queue->Rear);
// 断言 最好只写一个判断条件
// 如果有多个 选择if 或者 多行断言
queue->Front->next = NULL;
printf("InitQueue函数执行成功\n");
}

int isEmptyQueue(PQueue queue)
{
return queue->Front == queue->Rear;
}

void InsertQueue(PQueue queue, int val)
{
PNODE pnew = (PNODE)malloc(sizeof(NODE)); //临时节点
assert(pnew);

//先创建好节点 然后连接上去
pnew->element = val; //把数据放入数据域
pnew->next = NULL; //next指针 指向空

//尾插法
queue->Rear->next = pnew; //临时节点连上去
queue->Rear = pnew; //让队尾指向 '真的' 队尾
printf("数据%d入队成功\n", val);
}

void DeleteQueue(PQueue queue)
{
assert(!isEmptyQueue(queue)); //先判断 是否为空

PNODE ptemp = queue->Front->next;
// 把临时节点标记为头结点的next 因为头结点是空的
int val = ptemp->element; //保存值
queue->Front->next = ptemp->next; //让头结点指向临时节点的下一个

if (queue->Rear == ptemp) //如果只有一个元素 那么出队之后 头和尾连在一起了
queue->Rear = queue->Front;

free(ptemp); //释放
ptemp = NULL;
//防止ptemp变成野指针 实际上没大用 但是体现了有这样一个保护内存的想法
// 因为随着函数的释放 临时变量会跟着释放
printf("出队成功,出队数据=%d\n", val);
}

void DestroyQueue(PQueue queue)
{
while (queue->Front != NULL) //当头不为空时
{
queue->Rear = queue->Front->next;
free(queue->Front);
queue->Front = queue->Rear;
}

printf("DestroyQueue函数执行,队列销毁\n");
}

int main()
{
Queue queue;
InitQueue(&queue);
isEmptyQueue(&queue);

InsertQueue(&queue, 100);
InsertQueue(&queue, 200);
InsertQueue(&queue, 300);
InsertQueue(&queue, 400);
InsertQueue(&queue, 500);

isEmptyQueue(&queue);
DeleteQueue(&queue);
DestroyQueue(&queue);
return 0;
}

二十四:正则表达式

正则表达式I:
一. 基础概述
正则表达式是对字符串操作的一种逻辑公式.
事先定义好一些特定的字符以及这个写特地字符组合,组成一个"规则字符串"。
这个规则字符串用来表达对字符串的一种过滤.

二. 三种算法

1. 匹配算法
算法用于判断给出的表达式和给定字符系列之间,是否存在匹配.
如果存在返回true,否则返回false。
regex_match();
特殊符号: 描述
. 任意单个字符. abc a~c
[] 字符集
* 0个或多个
+ 一个或者多个
? 0个或者1个
{} 计数
| 或
^ 行的开始 or 否定 在字符集里面是否定的意思.
\ 格式字符
格式字符 描述

\d 十进制数字 0 - 9
\D 除了\d之外的字符
\w(小写) 一个字母(a-z A-Z 0-9)
\W(大写) 非字母 (a-zA-Z0-9)之外的

https://www.cr173.com/soft/22753.html (常用正则表达式)
2. 搜索算法
3. 替换算法

//1.匹配算法
#include<iostream>
#include <string >
//重点: 学会使用三种算法
//第一步: 添加头文件
#include <regex>
using namespace std;
int main()
{
1.日期格式 2018-03-29
regex e("\\d{4}-\\d{1,2}-\\d{1,2}");

string Str;
cin >> Str;
if (regex_match(Str, e))
{
cout << "这个是日期格式!" << endl;
}
else
{
cout << "这个不是日期格式!" << endl;
}

密码 字符开头 长度在6~18之间
regex e("[a-zA-Z]\\w{6,18}");

string Str;
cin >> Str;

if (regex_match(Str, e))
{
cout << "密码格式正确!" << endl;
}
else
{
cout << "密码格式错误!" << endl;
}
//以上是方法1

//以下是方法2

regex e("(\\w+)@(\\w+)(\\.(\\w+))");
string Str;
while (true)
{
cin >> Str;
//[1,9]
//3@qq.com.cn
if (regex_match(Str.begin(), Str.end(), e))
{
cout << "邮箱匹配成功!" << endl;
}
else
{
cout << "邮箱匹配失败!" << endl;
}
}
regex e("^[\u4e00-\u9fa5]{0,}$"); // 不是空格 0个或者多个

if (regex_match("我系渣渣辉", e))
{
cout << "匹配成功!" << endl;

}
else
{
cout << "匹配失败!" << endl;
}

return 0;
}


//2.搜索算法:
#include<iostream>
#include<regex>
#include<string>
#include<fstream>
using namespace std;
int main()
{
string str("Hometown is the place where a person belongs. Chwealth
provides, I believe one should abandon the pursuit of materialism
and instead concentrate on thepursuit of happiness.");

//找d开头的单词
regex e("\\b(d)[^ ?.]*");//^表示否定
smatch m; //smatch 存放string类型的结果
cout << "搜索到文章中以‘d’开头的单词有:"<<endl;
while (true)
{

if (regex_search(str, m, e))
{
cout << m.str() << " " << endl;
str = m.suffix().str();
}
}

string str;

fstream File("英语作文.text", ios::in | ios::binary);

//知道文件大小

File.seekg(0, SEEK_END);
size_t a = File.tellg(); //获取文件指针的位置
File.seekg(0, SEEK_SET);

char szTemp[10240] = { 0 };
str += szTemp;
File.read(szTemp, sizeof(string)*a);
File.close();
regex e("\\b(Most)[^ .?]*");
string S;
regex_replace(back_inserter(S), str.begin(), str.end(),e,"Baby");
cout << S << endl;

cout << regex_replace(str, e, "$&", regex_constants::format_no_copy);
return 0;
}


二十五:项目一推箱子
#include <stdio.h>
#include <conio.h> //控制台输入输出的头文件
// getch();
#include <graphics.h> //头文件 必须要安装图形库
//图形库的头文件
//播放音乐的头文件
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib") //一个静态库


/*
一个新的贴图方法
透明遮罩法
透明色彩法
*/

int main()
{
initgraph(550,550,0); //创建一个窗口 长 宽
// 颜色 RGB自己规定三个值确定颜色
// 可以直接调用预留的宏
// 设置颜色 字体 背景 线条
setbkcolor(GREEN); //设置背景颜色
cleardevice(); //刷新屏幕 使用设置的背景色
// 画图形
setlinecolor(RED); //设置线条颜色
setfillcolor(BLUE); //设置填充颜色
//circle(100, 100, 50); //无填充 有线条
//solidcircle(200, 200, 50);//有填充 无线条
//fillcircle(100, 200, 50);// 有填充 有线条
//图案有三种
//贴图
//要改字符集
// 项目 属性 常规 字符集 改成多字符集
IMAGE img; //定义一个变量 图片类型
//loadimage(&img,"./res/XiaQi.jpg",0);
//loadimage(&img, "./res/XiaQi.jpg", 550,550);
/* 载入图片 参数意义
放到哪一个图形变量 图片的路径 图片的大小(0表示自适应)
也就是图片本身的大小
路径怎么写 如果是cpp同目录 直接给出图片名字
如果在同目录下的另一个文件夹
用./进入文件夹 再用/给出图片名字
./res./res1/xiaqi.jpg
如果返回上级目录 用..
如果不会写地址 给绝对路径也可以*/
//putimage(0,0,&img);
// 贴的图片的左上角 贴哪张图
//loadimage(0, "./res/XiaQi.jpg", 0);
//如果loadimage的第一个参数不给出变量地址
//直接给0 可以直接贴图

mciSendString("open ./res/bgm.mp3 alias bgm",0,0,0);
// alias 用来取别名 给音乐取别名
// 打开音乐 上面有头文件
mciSendString("play bgm repeat",0,0,0);
// repeat表示重复播放
//如果有下载的途径 可以用wav 导入资源文件
PlaySound((LPCTSTR)资源名,GetModuleHandle(NULL),SND_RESOURCE| SND_ASYNC |SND_LOOP );
// SND_RESOURCE 表示从资源中获取
//SND_ASYNC 异步播放
//SND_LOOP 循环播放
//播放音乐

getchar();
closegraph(); //关闭窗口
return 0;
}

//地图
int Map[8][8] = {
0, 0, 0, 1, 1, 1, 0, 0,
0, 0, 0, 1, 3, 1, 0, 0,
1, 1, 1, 1, 0, 1, 0, 0,
1, 3, 4, 0, 4, 1, 1, 1,
1, 1, 1, 5, 4, 0, 3, 1,
0, 0, 1, 4, 1, 1, 1, 1,
0, 0, 1, 3, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0, 0
};

void Game_InitMap();//加载图片
void Game_Paint(); //贴图
void Game_play(); //操作
int Game_Judgement(); //判断游戏是否结束

// 因为要贴图 所以要给出变量
// 可以用图片数组 把图片放在数组里
IMAGE BackImage, WallImage; //背景 墙
IMAGE Box1, Box2; //箱子
IMAGE Tag1, Tag2; //目的地
IMAGE Boom1, Boom2; //箱子推到目的地
IMAGE Per1, Per2; //人
IMAGE END; //奖励
//IMAGE a[5]; //用数组放多张图也是可以的

void Game_InitMap()
{
loadimage(&BackImage, "./res/Background.jpg", 550, 550);//背景
loadimage(&WallImage, "./res/Wall.jpg", 69, 69);//墙壁

loadimage(&Box2, "./res/Box.jpg", 69, 69);//箱子

loadimage(&Tag2, "./res/Tag.jpg", 69, 69);//目的地

loadimage(&Boom2, "./res/Boom.jpg", 69, 69);//箱子到达目的地

loadimage(&Per2, "./res/Person.jpg", 69, 69);//人物

loadimage(&END, "./res/XiaQi.jpg", 550, 550);//胜利奖品

}

void Game_InitMap2(int live) //给出关卡数 用来判断是第几关
{
//因为白色为(255,255,255) 和背景做按位与操作 得到背景颜色 位与运算
// 255&120 1111 1111 &120 --- 120
//黑色是(0,0,0) 和背景做按位或运算 得到背景颜色
// 位或运算 只有同为0 才返回0
// 0| 120--- 0000 0000 | 120 120
loadimage(&BackImage,"./res/Background.jpg",550,550); //背景
loadimage(&WallImage,"./res/Wall.jpg",69,69 );//墙

loadimage(&Box1, "./res/Box1.jpg", 69, 69);//箱子白色
loadimage(&Box2, "./res/Box.jpg", 69, 69);//箱子

loadimage(&Tag1, "./res/Tag1.jpg", 69, 69);//目的地白色
loadimage(&Tag2, "./res/Tag.jpg", 69, 69);//目的地

loadimage(&Boom1, "./res/Boom1.jpg", 69, 69);//箱子到达目的地白色
loadimage(&Boom2, "./res/Boom.jpg", 69, 69);//箱子到达目的地

loadimage(&Per1, "./res/Person1.jpg", 69, 69);//人物白色
loadimage(&Per2, "./res/Person.jpg", 69, 69);//人物

loadimage(&END, "./res/XiaQi.jpg", 550, 550);//胜利奖品

//在这里添加关卡数
// 一个文件操作

//66--- 8*8 +2
//10*10 --- 10*10+2 102
FILE *fp = fopen("Map.txt", "r");
// 定义一个文件指针 fopen打开一个文件
// 第一对双引号给出文件名
// 第二对双引号表示打开方式 r read

if (NULL == fp) //如果打开失败 异常处理
{
MessageBox(NULL, "地图加载失败", NULL, NULL);
exit(0);
// exit 直接退出
}

int FileOffset; // 文件的偏移
FileOffset = 66 * (live - 1);
// 66 = 64+2--- 8*8+2
fseek(fp, FileOffset, SEEK_SET);
// 用来找到文件指针当前的位置
// 打开的哪一个文件 偏移量 文件开始的位置

int i, j;
char c;
//对地图的初始化
for (i = 0; i < 8; i++)
{
for (j = 0; j < 8; j++)
{
c = fgetc(fp);
//getc() 从文件中读出一个字符
//c = getchar();
Map[i][j] = c - '0';
// 因为是字符 所以要-48
}
}

fclose(fp); //关闭文件 先写 防止忘记

}
// 第一个要改的地方


//普通方法贴图
void Game_Paint()
{
for (int i = 0; i < 8; ++i)
{
for (int j = 0; j < 8; ++j)
{
switch (Map[j][i])
{
case 0: //空地
break;
case 1: //墙壁
putimage(69 * i, 69 * j, &WallImage);
break;
case 3://目的地
putimage(69 * i, 69 * j, &Tag2);
break;
case 4://箱子
putimage(69 * i, 69 * j, &Box2);
break;
case 5://人物
putimage(69 * i, 69 * j, &Per2);
break;
case 7://箱子在目的地上
putimage(69 * i, 69 * j, &Boom2);
break;
case 8://人物在目的地上
putimage(69 * i, 69 * j, &Per2);
break;
}
}
}
}

/*
0 空地
1 墙壁
3 目的地
4 箱子
5 人物
7 箱子在目的地
8 人在目的
*/

//透明遮罩法贴图
void Game_Paint2()
{
for (int i = 0; i < 8; ++i)
{
for (int j = 0; j < 8; ++j)
{
switch (Map[j][i])
{
case 0: //空地
break;
case 1: //墙壁
putimage(69 * i, 69 * j, &WallImage);
break;
case 3://目的地
putimage(69 * i, 69 * j, &Tag1, SRCPAINT); //做或运算
putimage(69 * i, 69 * j, &Tag2, SRCAND); //做与运算
break;
case 4://箱子
putimage(69 * i, 69 * j, &Box1, SRCPAINT);
putimage(69 * i, 69 * j, &Box2, SRCAND);
break;
case 5://人物
putimage(69 * i, 69 * j, &Per1, SRCPAINT);
putimage(69 * i, 69 * j, &Per2, SRCAND);
break;
case 7://箱子在目的地上
putimage(69 * i, 69 * j, &Boom1, SRCPAINT);
putimage(69 * i, 69 * j, &Boom2, SRCAND);
break;
case 8://人物在目的地上
putimage(69 * i, 69 * j, &Per1, SRCPAINT);
putimage(69 * i, 69 * j, &Per2, SRCAND);
break;
}
}
}
}

int Game_Judgement()
{
// 判断时间 判断步数
//方法1
// 因为地图是自己给的 目的地的位置是已知的
//if (map[1][5]== 7 && map[2][4] ==7)

// 方法2
//循环遍历整张图 判断有没有单独的箱子
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
if (Map[i][j] == 4) //判断 有没有落在空地上的箱子
{
return 1; //游戏还没有结束
}
}
}

//如果 用 多关卡的话
//
return 0;
}

void Game_play()
//第二个要改的地方
{
char ch; //用来获取键盘输入的信息
// ASCII码 和 键码 --- 等会讲
int x, y; //描述当前坐标
int live = 1; //描述关卡
Game_InitMap2(live); //贴图
while (true)
{
BeginBatchDraw();
//如果不用批量绘图 会闪屏
cleardevice();
putimage(0, 0, &BackImage);
//贴背景
mciSendString("open ./res/bgm.mp3 alias bgm",0,0,0);
mciSendString("play bgm repeat", 0, 0, 0);

if (!Game_Judgement()) //判断 每一关是否玩完了
{
live++; // 进入下一关
if (4 == live) // 1 +1 +1 +1
{
Game_Paint2();// 绘图
FlushBatchDraw(); //批量绘图 防闪屏
MessageBox(GetHWnd(), "闯关成功\r\n", "闯关提示", MB_OK);
// 弹出对话框
// 一个函数 作用
//提示语句
//菜单
// 按钮 ok 可以写两个 一个ok 一个取消
putimage(0, 0, &END);// 贴结束图
FlushBatchDraw();
Sleep(5000); //续1S 停留一秒
closegraph(); //关闭窗口
exit(0); //结束程序
// sleep 可以试一下自己的电脑 休眠之后能否打开
}
Game_InitMap2(live);
} // 出入平安
// 遍历地图 找到人在哪儿

//批量绘图要结束
Game_Paint2();
EndBatchDraw();

for (x = 0; x < 8; x++)
{
for (y = 0; y < 8; y++)
{ //如果人在目的地 或者 人在空地
if (Map[x][y] == 5 || Map[x][y] == 8)
{
break;
//goto SummerSeven;
}
}
if (Map[x][y] == 5 || Map[x][y] == 8)
{
break;
}
}

ch = _getch(); //获取键盘消息

//人在 Map[x][y]
switch (ch)
{
case 'w': //case 具有穿透性 如果第一个匹配成功之后没有遇到break 那么会继续往下匹配 直到break或者switch结束为止
case 72://向上
if (Map[x - 1][y] == 0 || Map[x - 1][y] == 3)
//表示上一个位置是目的地或者空地
{
Map[x][y] -= 5; //原来的地方的人不见了
// Map[x][y]=0; 因为不能确定当前位置是空地还是目的地
Map[x - 1][y] += 5; //人上去了
}
else if (Map[x - 1][y] == 4 || Map[x - 1][y] == 7)
//如果上面是一个箱子 或者是一个在目的地的箱子
{
if (Map[x - 2][y] == 0 || Map[x - 2][y] == 3)
//上面的上面是空地或者目的地
{
Map[x - 2][y] += 4; //箱子推到面的上面
Map[x - 1][y] += 1; //人到了上面
Map[x][y] -= 5; //人上去了
}
}
break;
case 75://向左
if (Map[x][y - 1] == 0 || Map[x][y - 1] == 3)
{
Map[x][y] -= 5;
Map[x][y - 1] += 5;
}
else if (Map[x][y - 1] == 4 || Map[x][y - 1] == 7)
{
if (Map[x][y - 2] == 0 || Map[x][y - 2] == 3)
{
Map[x][y - 2] += 4;
Map[x][y - 1] += 1;
Map[x][y] -= 5;
}
}
break;
case 80: // 往下
if (Map[x + 1][y] == 0 || Map[x + 1][y] == 3)
{
Map[x][y] -= 5;
Map[x + 1][y] += 5;
}
else if (Map[x + 1][y] == 4 || Map[x + 1][y] == 7)
{
if (Map[x + 2][y] == 0 || Map[x + 2][y] == 3)
{
Map[x + 2][y] += 4;
Map[x + 1][y] += 1;
Map[x][y] -= 5;
}
}
break;
case 77: //往右
if (Map[x][y + 1] == 0 || Map[x][y + 1] == 3)
{
Map[x][y] -= 5;
Map[x][y + 1] += 5;
}
else if (Map[x][y + 1] == 4 || Map[x][y + 1] == 7)
{
if (Map[x][y + 2] == 0 || Map[x][y + 2] == 3)
{
Map[x][y + 2] += 4;
Map[x][y + 1] += 1;
Map[x][y] -= 5;
}
}
break;
}
}
}


#include "resource.h"


int main()
{
initgraph(550,550);

Game_play();


//IMAGE img;
////loadimage(&img, "", 0, 0);
//loadimage(&img, "jpg", MAKEINTRESOURCE(101),550,550);
////导入资源文件之后贴图的函数
//// 第一个参数不变 第二个参数 表示图片种类
////第三个参数是 图片 ()内 给出图片的宏
//// 后两个参数 不变 0表示自适应 也就是本身大小
//putimage(0, 0, &img);
//// 导入资源文件之后的 音乐 ---- wav
//// 音乐导入之后 只能使用 PlaySound
//// 图片 jpg bmp wav ico
////PlaySound((LPCTSTR)IDR_WAV1,GetModuleHandle(NULL),
//// SND_RESOURCE | SND_ASYNC | SND_LOOP);
//// SND_RESOURCE 表示来自资源
//// SND_ASYNC 表示异步播放
//// SND_LOOP 表示循环播放

//getchar();
//closegraph();

// 为什么导入的函数 反而麻烦
// 因为发送给别人的时候 可以节省东西

//release 发布版本
// 将debug 改成release 要重新修改字符集

return 0;
}

 


二十六:项目二学生管理系统
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 字符串函数的头文件 strcmp
// 增 查 删 改
// 增 查 打印 输入
// 删除 修改

// 给出学生结构
typedef struct student
{
char name[20];
float height;
long int ID;
char sex;
char telNum[12];
int age;
struct student *next;
}STU,*PSTU;

void menu(PSTU head); //菜单
void getStuinfo(PSTU temp); //获取学生信息
void recordStu(PSTU head); //录入学生信息
void print(PSTU temp); // 打印函数
void printALL(PSTU head); //打印全部学生信息
PSTU FindbyName(PSTU head, char name[]);//通过名字查找
void changeStuName(PSTU head, char name[]);//通过姓名修改学院信息
void delStubyID(PSTU head, long int ID);//通过ID去删除一个节点


// 删除 要传入二级指针 头结点
// 用指针函数 返回head给主函数的head
// 二级指针 直接修改主函数的head

int main()
{
PSTU head = (PSTU)malloc(sizeof(STU));
head->next = NULL; //尾插法的火车头
//head = NULL;
menu(head);
return 0;
}

// 菜单函数
void menu(PSTU head)
{
while (1)
{
printf("=================菜单=================\n");
printf("1.录入学生信息\t\t\t2.查询学生信息\n");
printf("3.修改学生信息\t\t\t4.删除学生信息\n");
printf("5.打印学生信息\t\t\t6.退出\n");
printf("请输入你的选项\n");

switch (getchar()) //getchar 所以获取到的是字符
{
default: //MSDN标准说法是 写在最前面 有时候会更快
break;
case '1': //录入
recordStu(head);
break;
case '2':
//FindbyName(head, "Xiaqi");
{
printf("请输入要查找的名字:\n");
char name[20];
scanf_s("%s", name, 20);
PSTU temp = head;
do
{
//赋值运算符的优先级低 所以要括号
if (temp = FindbyName(temp, name))
{
print(temp); //打印单个节点
getchar();
}
} while (temp);
}
break;
case '3':
printf("请输入要修改信息的名字:\n");
char arr[20];
scanf_s("%s", arr, 20);
// 这个函数 只有字符串需要给第三个参数
// 表示限定输入的长度
changeStuName(head, arr);
break;
case '4':
printf("请输入要删除的信息的ID:\n");
long int ID;
scanf("%ld", &ID);
delStubyID(head, ID);
break;
case '5':
printALL(head);
getchar(); //阻塞
getchar();
break;
case '6':
return;// exit(0); 退出程序
break;
}
getchar();
system("cls");
}

}

//输入学生信息
void getStuinfo(PSTU temp)
{
printf("请输入个人信息:\n");
printf("请输入名字 ID 电话号码 身高 性别 年龄:\n");
scanf_s("%s %ld %s %f %c %d", temp->name, 20, &temp->ID, temp->telNum, 12, &temp->height, &temp->sex, 1, &temp->age);
// 字符串 可以不要取地址 也可以要
getchar(); //多输入一个回车才能输入结束
// 可以不写
}

//录入学生信息 插入节点
void recordStu(PSTU head)
{
PSTU temp = head;

PSTU p = (PSTU)malloc(sizeof(STU));
// 临时节点

//获得数据
getStuinfo(p);
// 以ID为标准排序
while (temp->next != NULL) //判断是否为空
{
// 以名字排序 strcmp
if (temp->next->ID > p->ID)
// 判断新插入节点该插在那个位置
// 判断当前节点的下一个节点的ID
{
break;
}
// 如果不成立
temp = temp->next; //后移一位
}
// 从小到大排列ID 大的ID在后面 尾插法
p->next = temp->next;
temp->next = p;
}

//打印单个节点
void print(PSTU temp) // 打印一个节点
{
printf("名字:%s\tID:%ld\t电话号码:%s\n身高:%f\t
性别:%c\t年龄:%d\n",temp->name,temp->ID,temp->telNum,
temp->height,temp->sex,temp->age);

}

//打印全部学生信息
void printALL(PSTU head)
{
PSTU temp = head->next; // 第一个是火车头没有数据
printf("所有学生信息如下:\n");
while (temp != NULL)
{
print(temp); //打印
temp = temp->next;//指向下一个
}
}

// 通过姓名查找
PSTU FindbyName(PSTU head, char name[])
{
PSTU temp = head;

while (temp->next != NULL)
{
if (!strcmp(temp->next->name, name))
{
return temp->next;
}
temp = temp->next;
}
return temp->next; // 实际上 NULL
}

//修改成员信息
void changeStuName(PSTU head, char name[])
{
PSTU temp = head;

while (temp->next != NULL)
{
// if (temp->next->ID>ID)
if (!(strcmp(temp->next->name, name)))
{
print(temp->next);// 打印原有信息
printf("请输入修改后的值:\n");
getStuinfo(temp->next); //输入信息
return;
}
temp = temp->next;
}

}

//删除节点
void delStubyID(PSTU head, long int ID)
{
PSTU p = head;
while(NULL != p->next)
{
if (ID == p->next->ID)
{
PSTU a = p->next;
p->next = p->next->next;
free(a);
return;
}
p = p->next;
}
if (p->next == NULL)
{
printf("输入的ID有误,请重新输入\n");
getchar();
getchar();
}
}

posted @ 2018-08-20 20:02  对弈  阅读(233)  评论(0编辑  收藏  举报