C语言详解
文章目录
第一章 简介
特点:
-
语言简洁、紧凑,使用方便灵活;
-
运算符丰富;
-
数据类型丰富,具有现代语言的各种数据结构;
-
具有结构化的控制语句;
-
语法限制不太严格,程序设计自由度大;
-
C语言允许直接访问物理地址,能进行位(bit)操作,能实现汇编语言的大部分功能,可以直接对硬件进行操作;
-
生成目标代码质量高,程序执行效率高;
-
用C语言编写的程序可移植性好(与汇编语言比)。
组成:
一个c程序是由一个主程序main() 和若干个子程序组成
算法特性
计算机算法的五个特性是:
有穷性,算法必须能在执行有限个步骤之后终止;
确切性,算法的每一步骤必须有确切的定义;
输入项,一个算法有0个或多个输入;
输出项,一个算法有一个或多个输出;
可行性,每个计算步骤都可以在有限时间内完成。
环境:
程序编译环境
使用C语言要有两个东西:文本编辑器和c语言编译器 GUN的gcc编译器。
c语言文件是“.c”为后缀的文件。 通过gcc编译器将“.c”文件转换为windows可执行程序 “.exe” 。
dev c++ 5.11这个比较老的 但是稳定。devc 6
C程序是由各种令牌组成的。 令牌是关键字、标识符、常量、字符串值、或者是一个符号
分号;
C语言程序都是以“;”作为语句结束符。
注释
// 单行注释
/* */ 多行注释
标识字符
用来标识变量、函数。 标识符是由字母或下划线”_“开头,后面可以为数字 。标识符内不能出现其他的字符,标识符严格区分大小写。
关键字
已经在c语言底层被使用过的标识符为关键字,不能够再次使用作为标识符。
for ,while, char, float ,int, double,switch, case,default, if,else。
C中的空格
第二章 数据
数据类型
1.基本类型
(1) 整数类型
1字节 是8位bit 8位二进制数 只能表示256个数字。
类型 字节数 值范围
char 1字节 -128 到 127 或 0到255
unsigned char 1字节 0-255
signed char 1字节 -128 到 127
int 2或4字节 -32768 到 32767 或 -2,147,483,648到2,147,483,647
unsigned int 2或4字节
short 2字节 -32768 到 32767
unsigned short 2字节 0 到 65,535
long 4字节 -2,147,483,648到2,147,483,647
unsigned long 4字节 0到4,94,967,295
(2) 浮点类型
类型 存储大小 值范围 精度
float 4字节 1.2x10-38到3.4x1038 6位有效位
double 8字节 2.3x10308到1.7x10308 15位有效位
long double 16字节 3.4x104932到1.1x104932 19位有效位
2. 枚举类型
3. void类型
指没有可用的值(即空)。 作为返回值类型即函数类型。指针也可以指向空。
4. 派生类型
5.变量类型
(1). 变量定义
变量定义必须指定一个数据类型,并且包含该类型的一个变量或者一个变量列表。
我们在定义时可以对变量进行初始化,也可以不初始化。 系统在你创建一个变量时默认进行了一次赋值操作 将其设置为NULL (0)
(2).变量声明
(1).建立存储空间。例如, int a;// 既声明,又定义
(2).不需要建立存储空间 (使用一个关键字”extern“) 。例:extern int a; 只声明,但是不定义。
(3).c中的左右值(Lvalues和Rvalues)
左值:指向内存位置的表达式称为左值表达式。a = 10;标识字符基本代表左值。
右值:存储在内存中的数值。
a = 10; 是有效赋值
10 = 10; 是无效赋值
常量
(1) 整数常量
可以是十进制、八进制、十六进制的常量。
以前缀(在最前方出现)指定基数:
十六进制:0x或0X表示十六进制 如0xffff。由 0-9 、a-f组成;
八进制:0表示八进制,如0777 , 由0-7组成
二进制:0b或0B表示二进制 ,如0b101 代表十进制为5,由0,1组成
(2) 浮点常量
浮点常量可以用整数部分、小数点、小数部分和指数部分组成。
有两种形式:
(1)小数类型
(2) 指数类型
使用小数类型必须包含整数部分、小数点、小数部分。
使用指数类型表示必须包含小数点、指数。带符号的指数必须用e或E引入。
3.1415926 // 合法的小数
31414926e-5L // 合法的
510E // 不合法,指数不完整
.e55 // 不合法的,缺少整数或分数
(3) 字符常量
在内存中以ASCII码进行转义的。
下图为ASCII码表,主要记忆65-90,97-32,大小写英文转换相差32。即
// 如果想要将字母a转为大写的字母A输出我们可以这样写。
char c = 'a';
c -= 32;
printf("%c",c); // 输出结果为A
ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 |
---|---|---|---|---|---|---|---|
0 | NUT | 32 | (space) | 64 | @ | 96 | 、 |
1 | SOH | 33 | ! | 65 | A | 97 | a |
2 | STX | 34 | " | 66 | B | 98 | b |
3 | ETX | 35 | # | 67 | C | 99 | c |
4 | EOT | 36 | $ | 68 | D | 100 | d |
5 | ENQ | 37 | % | 69 | E | 101 | e |
6 | ACK | 38 | & | 70 | F | 102 | f |
7 | BEL | 39 | , | 71 | G | 103 | g |
8 | BS | 40 | ( | 72 | H | 104 | h |
9 | HT | 41 | ) | 73 | I | 105 | i |
10 | LF | 42 | * | 74 | J | 106 | j |
11 | VT | 43 | + | 75 | K | 107 | k |
12 | FF | 44 | , | 76 | L | 108 | l |
13 | CR | 45 | - | 77 | M | 109 | m |
14 | SO | 46 | . | 78 | N | 110 | n |
15 | SI | 47 | / | 79 | O | 111 | o |
16 | DLE | 48 | 0 | 80 | P | 112 | p |
17 | DCI | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 | 50 | 2 | 82 | R | 114 | r |
19 | DC3 | 51 | 3 | 83 | S | 115 | s |
20 | DC4 | 52 | 4 | 84 | T | 116 | t |
21 | NAK | 53 | 5 | 85 | U | 117 | u |
22 | SYN | 54 | 6 | 86 | V | 118 | v |
23 | TB | 55 | 7 | 87 | W | 119 | w |
24 | CAN | 56 | 8 | 88 | X | 120 | x |
25 | EM | 57 | 9 | 89 | Y | 121 | y |
26 | SUB | 58 | : | 90 | Z | 122 | z |
27 | ESC | 59 | ; | 91 | [ | 123 | { |
28 | FS | 60 | < | 92 | \ | 124 | | |
29 | GS | 61 | = | 93 | ] | 125 | } |
30 | RS | 62 | > | 94 | ^ | 126 | ` |
31 | US | 63 | ? | 95 | _ | 127 | DEL |
括在单引号中的,可以是一个普通字符如‘x’ ,也可以是一个转义字符如‘\n’,如果需要在字符中得到一个反斜杠需要使用一个反斜杠去转义另一个反斜杠即‘\ \’,
如果反斜杠遇到其他的符号,转义后还是其他符号,如果遇到字母或者数字则进行转义
(4) 字符串常量
括在双引号中,与字符常量类似,转义字符相同,如果字符串很长,可以使用空格进行分行,如果要使用多行书写字符串可以使用反斜杠进行换行(不是\n)。
(5) 定义常量
定义常量 一般使用大写字母。
两种方式:
1.使用#define预处理器 相当于给100贴了一个标签叫N。
#define N 100
2.使用const关键字。
// 使用const关键字需要标明数据类型如:
const int N = 100;
第三章 计算
(1) 算数运算符
-
“+”
把两个操作数相加。
A + B
-
“-”
把两个操作数相减。
A - B
-
“*”
把两个操作数相乘
A * B
-
“ / ”
把两个操作数相除
A / B
-
“%”
取模运算符,整除后的余数
A % B; // 例如 5 % 3 = 2; 3 % 2 = 1;
-
“++”
自增运算符,整数值加1.
A++; // 先运算后自增 ++A; // 先自增后运算
-
“—”
自减运算符,整数值减1.
A--; // 先运算后自减 --A; // 先自减后运算
(2) 关系运算符
-
“ == ”
检查两个操作数是否相等,如果相等则为1,要区分赋值运算符”=“ 和关系运算符”==“ 的区别。
A == B; // 0 代表不相等,1代表相等。 上式返回 0. A == A // 返回1
-
“ != ”
表示不等号,检查两个操作数是否不相等,如果相等则为0,反之为1.
A == B; // 0 代表相等,1代表不相等。
上式返回 1.
A == A // 返回0
-
“ > “
表示大于号,检查是否左值比右值大,如果左值大,则返回1。
1 > 2 // 返回0 2 > 1 // 返回1
-
“ < ”
表示小于号,检查是否右值大于左值,如果右值大,则返回1。
1 < 2 // 返回1 1 > 2 // 返回0
-
“ <= ”
表示小于等于,检查是否右值大于等于左值,如果右值大或等,则返回1。
2 <= 2 // 返回1 1 <= 2 // 返回1 2 <= 1 // 返回0
-
“ >= ”
表示大于等于,检查是否左值大于等于右值,如果左值大或等,则返回1。
2 >= 2 // 返回1 1 >= 2 // 返回0
(3) 逻辑运算符
1. “&&”
逻辑与运算符,如果两个操作数都为非0,则条件为真即返回1,如果有一个操作数为0,则返回0.
A && B; // 如果A为0 则不执行B
2. “ || ”
逻辑或运算符,如果两个操作数有一个为1则条件为真即返回1,只有两个操作数都为0时,才返回0.
A||B // 如果A 为 0, B为1 返回1.,如果前操作数为真,则不执行后操作。
-
“ ! ”
逻辑非运算符,即真为假,假为真。
只要A不是0,!A都为0 如果A为0,则!A 为1 假如A为真 !A 为假
(4).赋值运算符
-
“ = ”
简单的赋值运算符,即将右边的操作数赋给左边操作数。
-
“ += ”
加且赋值运算符,把右边的操作数加上左边的操作数赋值给左边的操作数。
a = 1;
b = 2;
b += a; // b = b + a = 3
-
“ -= ”
减且赋值运算符,把左边的操作数减去右边的操作数赋值给左边的操作数。
a = 1; b = 2; b -= a; // b = b - a = 1
-
“ *= ”
乘且赋值运算符,把左边的操作数乘上右边的操作数赋值给左边的操作数。
a = 1;
b = 2;
b *= a; // b = b * a = 2
5. “ /= ”
除且赋值运算符,把左边的操作数除右边的操作数赋值给左边的操作数。
a = 1;
b = 2;
b /= a; // b = b / a = 2
-
“ %= ”
求模且赋值运算符,求两个操作数的模赋值左边的操作数。
a = 1; b = 2; b %= a; // b = b % a = 0
(3) 三元运算符
1.“ ? :”
条件表达式,如果条件为真,则返回冒号前的操作数,反之,返回冒号后的操作数。可以代替第四章《选择》中的if-else语句。
1 > 2 ? printf("hello") : printf("world");
==
if(1>2){
printf("hello")
}else{
printf("world");
}
1 + 2 * 4 && 3 - 3 || 5 != 4
9 && 0 || 1
0 || 1
1
第四章 选择(分支)
对条件进行判断,看条件是否满足真,如果满足,则执行模块内的语句,反之不执行。
if(1 != 2) printf(xxx); // 执行
1. if语句
if语句由一个或多个表达式后跟一个或多个语句组成
if(条件A){
当条件满足真时,执行的语句。
}
2. if-else 语句
if语句后跟一个可选的else语句,如果要进行多种判断可以使用else if语句
if(条件A){
当条件A满足真时,执行的语句。
}else if(条件B){ // 可以有多个
当条件B满足真时,执行的语句。
}else{
当if和所有的else if都不满足条件时,执行的语句。
}
3. if 嵌套
if语句内的语句还有if语句
if(条件A){
当条件A满足真时,执行的语句。
if(条件C){
当条件C满足真时,执行的语句。
}
}else if(条件B){ // 可以有多个
当条件B满足真时,执行的语句。
}else{
当if和所有的else if都不满足条件时,执行的语句。
}
4. switch语句
一个switch语句允许测试一个变量等于多个值时的情况。 每个值称为一个case。
switch(变量A){
case 值A:{语句;break;/*break可选的*/}
case 值B:{语句;break;/*break可选*/}
default : 语句; // 当上述条件都不满足时执行,相当于else
/*default可选的*/
}
// 如果case语句中不加break则会执行当前case后面所有的语句。
5. switch嵌套
可以在switch语句中在使用另一个switch语句。
switch(变量A){
case 值A:{
switch(变量B){
case 值C:{
语句;
break;
}
case 值D:{
语句;
break;
}
}
break;/*break可选的*/}
case 值B:{语句;break;/*break可选*/}
default : 语句; // 当上述条件都不满足时执行,相当于else
/*default可选的*/
}
// 如果case语句中不加break则会执行当前case后面所有的语句。
第五章 循环
有的时候,我们可能需要多次执行同一块代码,一般情况,语句是按顺序执行的。
1.循环类型
(1) while循环
当给定条件为真时,重复语句或语句组。while会在执行循环语句前进行测试条件
a = 1,b=2;
while(a < b){
printf("hello");
a++;
}
(2) for循环
for(init;condition;increment){
执行语句;
}
// init 初始化条件,只会执行一次;condition 代表执行条件;increment 代表增条件。
(3) do……while(条件);循环
第一次执行时不检测条件,即先执行一次才检查条件。
do{
printf("hello");
}while(0);
(4) 嵌套循环
循环语句内嵌套循环语句,当执行内循环时,只有内循环结束后,才可以继续执行外循环。
// 以for循环为例
for(int a=0;a<10;a++){
for(int b=0;b<10;b++){
printf("DeHao");
}
}
2.循环控制语句
(1) break语句
终止循环或switch语句,执行break语句后,将继续执行紧接着循环或switch后的语句。
int a,b;
for(a=0;a<10;a++){
printf("\n");
for(b=0;b<10;b++){
printf("DeHao");
}
break;
}
(2) continue 语句
立即停止本次循环,重新开始下一次循环。他不适用switch语句。
int a,b;
for(a=0;a<10;a++){
printf("\n");
for(b=0;b<10;b++){
printf("DeHao");
}
continue;
printf("\nqqqq");
}
(3) 无限循环
可以使用ctrl + c 强行终止程序。
第六章 函数
每个C程序的是由一个主程序和若干个子程序组成。这个程序就是函数,主程序即main()函数。
c语言需要先进行函数声明。
定义函数
函数定义形式:
返回值类型 函数名(函数的参数){
// 具体内容
}
// 参数就像占位符。当你调用函数时传进来的参数叫实际参数。
// 声明时的参数叫做形式参数。
#include<stdio.h>
// 声明
int abc(int a,int b,int c);
// 求加法的子程序叫 abc
int main(){
int f,b,g;
f=b=g=1;
int a = abc(f,b,g);
printf("%d",a);
return 0;
}
// 定义
int abc(int a,int b,int c){
return a+b+c;
}
调用函数
创建c函数时会定义函数是做什么的,通过调用函数去完成已定义的任务。
当程序调用函数时,程序控制权交给被调用的函数。当函数的返回被执行或到达函数的结束括号时,会把控制权还给主程序。
int abc(int a,int b);
int main(){
int a=1;int b=2;
abc(a,b);
return 0;
}
// 在主程序中调用子程序进行使用子程序对应的操作。
函数参数
如果函数需要使用参数,必须声明接受参数值的变量,即形式参数。
形式参数是局部参数,只有在调用函数时被创建,函数结束时被销毁。
调用类型:
传值调用:
只把值传递过去并不改变原先的数值,相当于是把原先的值复制给形式参数,使形式参数具有这个数值。修改函数内的形式参数不会影响实际参数。
引用调用
通过指针传递方式,形参指向实参的地址。此时改变函数内的形式参数时相当于改变了形参所对应的地址的值即会改变实参。
第七章 函数 2
C作用域规则
作用域是程序中定义的变量所存在的区域,超过该区域变量就不能被访问。
局部变量
在某个函数或块内的局部声明的变量称为局部变量。它们只能被该函数或代码块内部的语句使用。
#include<stdio.h>
int main(){
/*
局部变量声明
此处的a、b、c只能在main函数内使用
*/
int a,b;
int c;
return 0;
}
全局变量
定义在函数外部,通常在程序的顶部。全局变量在整个程序生命周期内都是有效的,在任意的函数内部能访问全局变量。
#include<stdio.h>
/* 全局变量声明 */
int g;
int main(){
/* 局部变量声明 */
int a,b;
a = 20;
b = 10;
g = a + b;
printf("%d",g);
}
如果局部变量和全局变量的名称相同,在函数内,优先使用局部变量,不会使用全局变量。
#include<stdio.h>
/* 全局变量声明 */
int g = 20;
int main(){
/* 局部变量声明 */
int g = 10;
printf("%d",g); //10
return 0;
}
形式参数
函数的参数,形式参数,被当作该函数内的局部变量,如果与全局变量同名它们会优先使用
#include<stdio.h>
int sum(int a,int b);
/* 全局变量声明 */
int a = 20;
int main(){
/* 局部变量声明 */
int a = 10;
int b = 10;
int c;
printf("%d",a);
c = sum(a,b);
printf("%d",c); //10
return 0;
}
int sum(int a,int b){
printf("%d",a);
retrun a + b;
}
第八章 数组
数组是用来存储一系列数据,但他往往被认为是一系列相同类型的变量。
数组的声明不是一个个独立的声明,而是声明一个数组变量,pub[99]。
所有的数组都是由连续的内存位置组成。最低位置对应第一个元素,以此类推。
数组中的特定元素可以通过索引访问,第一个索引值为0.
声明数组
声明数组需要指定元素的类型和元素的数量。
type arrayName[arraySize];
这叫做一维数组,arraySize必须是一个大于零的整数常量,type可以是任意数据类型。
// 声明一个类型为int的含有10个元素的banana数组
int banana[10];
初始化数组
可以诸葛初始化数组,也可以使用一个初始化语句。如下
int banana[10] = {100,10,19,18(……)};
花括号“{}”直接的值的数目不能多于我们在数组声明时方括号“[]”中指定的元素数目。
如果你省略掉了数组的大小,数组的大小则为初始化时元素的个数。
int banana[] = {100,10,19,18}; // 此时数组的大小就是4
所有的数组都是以0作为第一个元素的索引,也被称为基索引,最后一个元素索引是数组的总大小减一。
banana[3] - 100; // 该语句是将数组中的第四个元素赋为100。
访问数组
数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内“[]”,跟在数组名称后面。如
#include<stdio.h>
int main(){
int banana[10];
int i,j;
for(i=0;i<10;i++){
banana[i] = i + 100;
}
for(i=0;i<10;i++){
printf("%d",banana[i]);
}
return 0;
}
第九章 数组 2
二维数组
多维数组最简单的形式为二维数组。在本质上,二维数组是一个一维数组的数组。声明一个x行y列的二维数组形式:
type arrayName[x][y]
一个三行四列的数组的格式
column0 | column1 | column2 | column3 | |
---|---|---|---|---|
Row0 | a[0] [0] | a[0] [1] | a[0] [2] | a[0] [3] |
Row1 | a[1] [0] | a[1] [1] | a[1] [2] | a[1] [3] |
Row | a[2] [0] | a[2] [1] | a[2] [2] | a[2] [3] |
初始化二维数组
多维数组可以通过在括号内为每行指定值来进行初始化。下面是一个三行四列的数组。
int a[3][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};
// 内嵌的括号是可选的
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; // 与上面是等同的
访问二维数组
二维数组中的元素是通过使用下标(即数组的行索引和列索引)来访问的。
#include<stdio.h>
int main(){
int a[5][2] = {{0,1},{2,3},{3,4},{4,5}{5,6}};
int i,j;
for(i=0;i<5;i++){
for(i=0;i<2;i++){
printf("%d ",a[i][j]);
}
printf("\n");
}
return 0;
}
第十章 游戏
扫雷游戏
相信大家都玩过扫雷游戏,就是在一个棋盘内点击格子,通过出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。现在我们用C语言实现一个简易的扫雷游戏。游戏的实现非常的简单,主要涉及了C语言中的一些基本知识,循环与分支、函数、数组等,非常容易上手。
设计思路
建立游戏菜单
写一个简单的菜单,实现的功能就是输入1进入游戏,输入0退出游戏,输入其他数字就显示错误,重新选择。
菜单代码,使用了do while循环来实现菜单的循环打印,因为菜单在进入游戏后就需要打印,还有就是在选择错误和游戏结束时也需要打印。switch实现菜单的选择功能。
建立好菜单后我们要建立棋盘。假设我们建立 9 * 9 的游戏棋盘,那我们真正就要建立 11 * 11 的棋盘。如图,绿色区域就是要扫雷的区域,在绿色区域才会有雷。之所以要建立大一圈的棋盘,是为了,后面检测扫雷地址周围雷数目时不越界。
棋盘建立用二维数组来实现,并且要建两个二维数组,mine用以放置布置雷的信息,show用于放置排查雷的信息,这样做可以方便的显示排查地址周围雷的数量。使用define定义后期想改变棋盘大小更方便。
初始化棋盘
将mine初始化为‘0’,show初始化为‘*’,这样做是为了让埋雷、计算雷的数量、棋盘显示更方便,接着往下看你就会发现。
布置雷
雷是布置在mine数组里的,EASY_COUNT是雷的总个数。前面已经把mine初始化为‘ 0 ’了,现在只需将要放雷的位置的内容改为’1’就可以了。这个位置是随机产生的,这里使用了srand函数和rand函数来生成随机数。雷的放置坐标区域是[1][1]到[9][9],也就是上面的绿色区域。所以rand() % row得到0~8的数,要加上1。
排查雷
输入1~9数字坐标,然后判断此坐标是否为雷,是雷游戏结束。不是雷就计算此坐标周围雷的个数,放入show数组中然后打印show数组。玩家可以通过反馈的信息再进行排雷。
雷个数的计算方式就是把坐标周围8个字符加起来再减去8个‘0’,得到的就是雷的个数再以字符的形式打印。
变量win是存放排除的格子的个数,当win等于格子总数减掉雷的个数时表示排除了所有雷,游戏获胜。
代码
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);
//打印一下棋盘
void DisPlayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
void mnue();
void game();
int main()
{
srand((unsigned int)time(NULL));
int input;
do
{
mnue();
printf("请选择-->");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏");
break;
default:
printf("选择错误,请重新选择-->");
}
}while (input);
return 0;
}
void mnue();
{
printf("*******************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("*******************************\n");
}
void game()
{
char mine[ROWS][COLS] = {0};//存放布置雷的信息
char show[ROWS][COLS] = {0};//存放排查雷的信息
//初始化棋盘
InitBoard(mine, ROWS, COLS,'0');//初始化为‘0’
InitBoard(show, ROWS, COLS,'*');//初始化为‘*’
//打印一下棋盘
DisPlayBoard(show, ROW, COL);
//布置雷
SetMine(mine,ROW,COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)//初始化棋盘
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] =set;
}
}
}
void DisPlayBoard(char board[ROWS][COLS], int row, int col)//打印棋盘
{
int i = 0;
int j = 0;
printf("-------------- 扫雷游戏 --------------\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-------------- 扫雷游戏 --------------\n");
}
void SetMine(char mine[ROWS][COLS], int row, int col)//布置雷
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0');
{
mine[x][y] = '1';
count--;
}
}
}
static int get_mine_count(char mine[ROWS][COLS], int x, int y)//计算周围雷的数量
{
return mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1]-8*'0';
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)//排查雷
{
int x = 0;
int y = 0;
int win = 0;//排除的非雷格子数
while (win<ROW*COL- EASY_COUNT)
{
printf("请输入要排查的坐标->:");//输入排查的坐标
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法性
{
if (mine[x][y] == '0')//判断是否为雷
{
//不是雷,检查周围雷的情况
int count=get_mine_count(mine, x, y);
show[x][y] = count + '0';
DisPlayBoard(show, ROW, COL);//打印棋盘信息,显示周围雷的个数
win++;
}
else
{
printf("你个菜鸡,你被炸死了\n");//是雷
DisPlayBoard(mine, row, col);
break;
}
}
else
printf("输入坐标错误,请重新输入->:");
}
if (win == ROW * COL - EASY_COUNT)
{
printf("恭喜你!排雷成功!");
}
}
第十一章 指针
什么是指针?
指针也就是内存地址,指针变量是用来存放内存地址的变量。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type* varname;
// type指的变量类型,varname是变量名
所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。
不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
如何使用指针?
使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 *****来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:
#include <stdio.h>
int main (){
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("var 变量的地址: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("ip 变量存储的地址: %p\n", ip );
/* 使用指针访问值 */
printf("*ip 变量的值: %d\n", *ip );
return 0;
}
第十二章 指针 2
略
第十三章 链表
略
第十四章 文件
打开文件
您可以使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象,类型 FILE 包含了所有用来控制流的必要的信息。下面是这个函数调用的原型:
FILE *fopen( const char *filename, const char *mode );
//filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:
模式 | 描述 |
---|---|
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
关闭文件
为了关闭文件,请使用 fclose( ) 函数。函数的原型如下:
int fclose( FILE *fp );
如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 stdio.h 中的常量。
C 标准库提供了各种函数来按字符或者以固定长度字符串的形式读写文件。
写入文件
下面是把字符写入到流中的最简单的函数:
int fputc( int c, FILE *fp );
函数 fputc() 把参数 c 的字符值写入到 fp 所指向的输出流中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回 EOF。您可以使用下面的函数来把一个以 null 结尾的字符串写入到流中:
int fputs( const char *s, FILE *fp );
函数 fputs() 把字符串 s 写入到 fp 所指向的输出流中。如果写入成功,它会返回一个非负值,如果发生错误,则会返回 EOF。您也可以使用 int fprintf(FILE *fp,const char *format, …) 函数把一个字符串写入到文件中。
读取文件
下面是从文件读取单个字符的最简单的函数:
int fgetc( FILE * fp );
fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF。下面的函数允许您从流中读取一个字符串:
char *fgets( char *buf, int n, FILE *fp );
函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。
如果这个函数在读取最后一个字符之前就遇到一个换行符 ‘\n’ 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。您也可以使用 int fscanf(FILE *fp, const char *format, …) 函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取。
注:常用的一些函数与考试常见点
getchar()与scanf()的区别
scanf函数是格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量中。
getchar函数是键盘输入函数,其功能是从键盘上输入一个字符。
scanf函数在读取数字时会跳过空格、制表符和换行符。
getchar函数只能输入字符型,输入时遇到回车键才从缓冲区依次提取字符。
putchar()与printf()的区别
printf 可以输出一个任意的字符串,还可以有参数,而putchar只能输出一个字符。
printf 的返回值是正常输出的参数的数量,而 putchar则是是否正常输出。
简言之:printf功能更强大 可以输出各种格式。putchar只能输出单个字符。因为字符的处理很特殊,所以专门有对字符的处理,如 putchar ,getchar 之类的。
牛顿迭代法
这个算法还是挺重要的(可能出填空,主要记住如何求解)
#include<stdio.h>
#include<math.h>
double func(double x) //举例函数
{
return x*x*x*x-3*x*x*x+1.5*x*x-4.0;
}
double func1(double x) //导函数
{
return 4*x*x*x-9*x*x+3*x;
}
int Newton(double *x,double precision,int maxcyc) //maxcyc 迭代次数
{
double x1,x0;
int k;
x0=*x;
for(k=0;k<maxcyc;k++)
{
if(func1(x0)==0.0)//若通过初值,函数返回值为0
{
printf("迭代过程中导数为0!\n");
return 0;
}
x1=x0-func(x0)/func1(x0);//进行牛顿迭代计算
if(fabs(x1-x0)<precision || fabs(func(x1))<precision)//达到结束条件
{
*x=x1; //返回结果
return 1;
}
else //未达到结束条件
{
x0=x1; //准备下一次迭代
}
}
printf("迭代次数超过预期!\n"); //迭代次数达到,仍没有达到精度
return 0;
}
int main()
{
double x,precision;
int maxcyc;
printf("输入初始迭代值x0:");
scanf("%lf",&x);
printf("输入最大迭代次数:");
scanf("%d",&maxcyc);
printf("迭代要求的精度:");
scanf("%lf",&precision);
if(Newton(&x,precision,maxcyc)==1) //若函数返回值为1
{
printf("该值附近的根为:%lf\n",*x);
}
else //若函数返回值为0
{
printf("迭代失败!\n");
}
return 0;
}