枚举(理解)

什么是枚举

把一个事物所有可能的取值一一列举出来

怎样使用枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# include<stdio.h>

enum WeekDay //enum是枚举的意思.跟结构体一样,只定义了一个数据类型,并没有定义变量,该数据类型的名字是 enum WeekDay
{
MonDay, Tueday, WednesDay, TursDay, FriDay, SaturDay, Sunday
}; //别忘记;号

int main(void)
{
//int day; //day定义成int类型不太合适,取值范围太大

enum WeekDay day = SaturDay;
printf("%d\n", day);


return 0;
}


枚举的优缺点

代码更安全
书写麻烦

位运算符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
&    按位与
&& 逻辑与 也叫并且
&&与& 的含义完全不同

&:
规则:只有对应的两个二进位都为1时,结果位才为1
1&1=1
1&0=0
0&1=0
0&0=0

21&7=5 //0001 0101 & 0000 0111 = 0000 0101 = 5

&&:
k = i && j; //k的值只能是1或0,因为&&是逻辑运算符,逻辑运算符的结果只能是真或假,在C中,真用1表示,假用0表示
printf("%d\n", k);

| 按位或
||逻辑或
|按位或

规则:
1|0 = 1
1|1 = 1
0|1 = 1
0|0 = 0

~ 按位取反
~i就是把i变量所有的二进制位取反

^ 按位异或
相同为零
不同为1
1^0 = 1
0^1 = 1
1^1 = 0
0^0 = 0

<< 按位左移 (重要)
i<<3 表示把i的所有二进制位左移3位,右边补0
左移n位相当于乘以2的n次方

考试题目:
A: i = i*8;
B: i = i<<3;

请问上述两个语句,哪个语句执行的速度快
答案:B快

>> 按位右移
i>>3 表示把i的所有二进制位右移3位,左边一般是0,根据最高位补或者全部补0.
右移n位相当于除以2的n次方,前提是数据不能丢失

位运算符的现实意义:通过位运算符我们可以对数据的操作精确到每一位

补码(次等重要)

原码

也叫 符号-绝对值码
最高位0表示正,1表示负,其余二进制位是该数字的绝对值的二进制位

原码简单易懂,加减运算复杂
存在加减乘除四种运算,增加了CPU的复杂度
零的表示不唯一

反码

反码运算不便,也没有在计算机中应用

移码

移码表示数值平移n位,n称为移码量
移码主要用于浮点数的阶码的存储

补码(重点):解决整数的存储

已知十进制求二进制

求正整数的二进制

除2取余,直至商为零,余数倒序排列

求负整数的二进制

先求与该负数相对应的正整数的二进制代码,然后将所有位取反,末尾加1,不够位数时,左边补1

零转二进制

全是零

已知二进制转十进制

  • 如果首位是0,则表明是正整数,按普通方法来求

  • 如果首位是1,则表明是负整数
    将所有位取反,末尾加1,所得数字就是该负数的绝对值

为什么负整数位数不够需要前面补1?

当正整数:比如十进制中的1,用二进制表示01,当二进制转十进制的时候,你只需要输入01,系统会自动在01前头补30个0来凑够32位.

当你是十进制中的-1时,用二进制表示的时候,先取十进制-1的绝对值1的二进制,这时候要注意,系统需要的是32位.你必须手动在前头补30个1,否则系统会自动给你补30个0,导致二进制的11被认为是正整数3,而不是-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# include <stdio.h>

int main(void)
{

int i = 0xFFFFFFEF; //FFFF是两个字节的16进制数,表示2的16次方,系统需要凑够32位.所以负数前面有多个F
printf("%d\n", i);


return 0;
}

在VS中的输出结果是:
-17

比如负整数1001010忽略前面补位,化为十进制
取反:0110101
末尾加1: 0110110
2的5次方+2的4次方+2的2次方+2的1次方=54
因为计算机看首位判断正负,1001010前面是1,所以为负
所以结果为-54

  • 如果全是零,则对应的十进制数字就是零

学习目标:
在Vc++6.0中一个int类型的变量所能存储的数字的范围是多少
int类型变量所能存储的最大整数十六进制最大的整数是:7FFFFFFF
int类型变量所能存储的绝对值最大的负整数用十六进制表示是:80000000

绝对值最小负数的二进制代码是多少
最大正数的二进制代码是多少
已知一个整数的二进制代码求出原始的数字
数字超过最大正数会怎么样
不同类型数据的相互转化

最重要是把这张图学明白

进制转换(最重要)

生活中遇到进制的例子

  • 一周一天: 七进制
  • 一年十二个月: 十二进制
  • 一小时六十分: 六十进制
  • 电脑中的数据: 二进制

预备知识:
小数除以大数,则商为零,余数是小数本身

如:
1/2=0 余数1
2/2=1 余数0
3/2=1 余数1

十进制转r进制:

总结:
方法:除r取余,直至商0,余数倒序排列

注意:不要用传统除法,用短除法

例十进制1000转化成16进制

1
2
3
4
5
6
7
8
9
10
11
12
# include <stdio.h>


int main(void)
{
int i =1000;
printf("%#X", i); //%#x比%x更好,因为在前面加上OX提示16进制,比3E8更明显

return 0;
}


r进制转为十进制

二进制与十六进制的转化

因为16是2的4次方,所以转化成16进制需要4位,不够补0

十六进制与二进制的转化

二进制与八进制相互转化

因为8是2的3次方,所以需要3位,不够补0

十六进制与八进制相互转化

不存在十六进制与八进制的直接相互转化,都是以二进制我中间进制来进行转化的

二进制全部位零的含义 –00000000000000的含义

  1. 数值零
  2. 字符串结束标识符’\0’
  3. 空指针NULL
    NULL本质也是零,而这个零不代表数字零,而表示的是内存单元的编号零

我们计算机规定了,以零为编号的存储单元的内容不可读,不可写

字符串的处理(了解)

链表:连结C语言和数据结构的知识

算法

通俗定义:解题的方法和步骤

狭义定义:对存储数据的操作
对不同的存储结构,要完成某一个功能所执行的操作是不一样的
比如:
要输出数组中所有的元素的操作和
要输出链表中所有元素的操作肯定是不一样的

这说明:
算法是依附于存储结构的
不同的存储结构,所执行的算法是不一样的

广义定义:广义的算法也叫泛型:无论数据是如何存储的,对该数据的操作都是一样的

我们至少可以通过两种结构来存储数据:
数组:
优点:存取速度快
缺点:插入和删除元素的效率很低

链表:

专业术语:
头结点
头结点的数据类型和首节点的类型是一模一样的
头结点是首节点前面的那个节点
头结点并不存放有效数据
设置头结点的目的是为了方便对链表的操作

头指针
存放头结点地址的指针变量

首节点
存放第一个有效数据的节点

尾节点
存放最后一个有效数据的节点

确定一个链表需要一个参数:头指针

链表的优缺点:
优点:插入删除元素效率高
不需要一个连续的很大的内存

缺点:查找某个位置的元素效率低

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# include <stdio.h>


//定义了一个链表节点的数据类型
struct Node
{
int data; //数字域
struct Node* pNext; //指针域
};

//函数声明
struct Node* CreateList();
void TraverseList();

int main(void)
{
struct Node *pHead; //pHead用来存放链表头结点的地址

pHead = CreateList(); //createlise()功能:创建一个非循环单链表,并将该链表头结点的地址赋给了pHead
TraverseList(pHead);

return 0;
}

对链表操作(遍历)

1
2
3
4
5
6
7
8
9
10
11
12
13
void traverse_list(struct Node * pHead)
{
struct Node * p = pHead->pNext;

while (NULL != p)
{
printf("%d\n", p->data);
p = p->pNext;
}
return;
}


文件的输入和输出

文件是什么

  • 所谓”文件”是指一组相关数据的有序集合.这个数据集有一个名称,叫做文件名.实际上在前面的各章我们已经多次使用了文件,例如原程序文件(.c),目标文件(.obj),可执行文件(.exe),库文件(头文件等)

  • 文件通常是驻留在外部介质(如磁盘等)上的,在使用时才调入内存中来.
    从不同的角度可对文件作不同的分类.从用户的角度看,文件可以分为普通文件和设备文件两种.

操作系统是以文件为单位对数据进行管理的

文件的分类

从用户观点:

  • 特殊文件(标准输入输出文件或标准设备文件)
  • 普通文件(磁盘文件)

从操作系统的角度看

每一个与主机相连的输入,输出设备看作是一个文件

例:
输入文件:终端键盘 输出文件:显示屏和打印机

按数据的组织形式:

  • ASCII文件(文本文件):每一个字节放一个ASCII代码
  • 二进制文件:把内存中的数据按其在内存中的存储形式原样输出到磁盘存放

例如整数10000D(D表示十进制输出)在内存中的存储形式以及分别按ASCII码形式和二进制形式输出如下图所示:

ASCII文件和二进制文件的比较:

  • ASCII文件便于对字符进行逐个处理,也便于输出字符.但一般所占存储空间较多,而且要花费转换时间

  • 二进制文件可以节省外存空间和转换时间,但一个字节并不对应一个字符,不能直接输出字符形式.

一般中间结果数据需要暂时保存在外存上,以后又需要输入内存的,常用二进制文件保存.

C语言对文件的处理方法

  • 缓冲文件系统:系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区.用缓冲文件系统进行的输入输出又称为高级磁盘输入输出.
  • 非缓冲文件系统:系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区.用非缓冲文件系统进行输入输出又称为低级输入输出系统.

课外知识

  • 在UNIX系统下,用缓冲文件系统来处理文本文件,用非缓冲文件系统来处理二进制文件
  • ANSIC标准(国际C语言标准)只采用缓冲文件系统来处理文本文件和二进制文件
  • C语言中对文件的读写都是用库函数来实现

文件的打开与关闭

文件型指针变量:
FILE *fp;

fp是一个指向FILE类型结构体的指针变量

我们使fp指向某一个文件的结构体变量,从而通过该结构体变量中的文件信息,能够访问该文件.

如果有n个文件,一般应设n个指针变量,使它们分别指向n个文件,以实现对文件的访问.

FILE类型的数组:
FILE f[5]; 定义了一个结构体数组f,它有5个元素,可以用来存放5个文件的信息.

文件的打开

文件的打开(fopen函数)

函数调用:

1
2
FILE *fp;
fp = fopen //(文件名,使用文件方式);

注意:
需要打开的文件名,也就是准备访问的文件的名字
使用文件的方式(“读”还是”写”等);
让哪一个指针变量指向被打开的文件

文件的使用方式

一般常用的:
“r” 只读
“w” 只写

对于文件使用方式有以下几点说明

  • 凡用”r”打开一个文件时,该文件必须已经存在,且只能从该文件读出

  • 用”w”打开的文件只能向该文件写入.若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已经存在,则将该文件删去,重建一个新文件

  • 若要向一个已存在的文件追加新的信息,只能用”a”方式打开文件.但此时该文件必须是存在的,否则将会出错.

  • 在打开一个文件时,如果出错,fopen将返回一个空指针NULL

  • 在程序中可以用这一信息来判别是否完成打开文件的工作,并作相应的处理.

  • 把一个文本文件读入内存时,要将ASCII码转换成二进制码,而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读写要花费较多的转换时间.对二进制文件的读写不存在这种转换.

文件打开示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# include <stdio.h>
# include <stdlib.h>

void main()
{
FILE* fp;
if (!(fp = fopen("D:\\geyouyang.txt", "w"))) //注意要双"\"
{
printf("不能够打开D:\\geyouyang.txt\n");
system("pause");

}
else
{
printf("成功打开!\n");
}
}

文件的关闭(fclose函数)

函数调用:
fclose (文件指针);

函数功能:
使文件指针变量不指向该文件,也就是文件指针变量与文件”脱钩”,以后不能再通过该指针对原来与其相联系的文件进行读写操作.

返回值:
关闭成功返回值为0;否则返回EOF(-1)

文件的读写

  • 对文件的读和写是最常用的文件操作.
    在C语言中提供了多种文件读写的函数:
  • 字符读写函数: fgetc和fputc
  • 字符串读写函数: fgets 和 fputs
  • 数据块读写函数: fread 和 fwrite
  • 格式化读写函数: fscanf和 fprintf

使用以上函数都要求包含头文件stdio.h

字符读写函数: fgetc和fputc

一,字符输入输出函数(fputc()和fgetc())

fputc()函数调用:

fputc(ch’fp);

函数功能:
将字符 (ch的值)输出到fp所指向的文件中去

对于fputc函数的使用要说明几点

  • 用写或读写方式打开一个已经存在的文件时将清除原有的文件内容,写入字符从文件首开始.如需保留原有文件内容,希望写入的字符以文件末开始存放,必须以追加方式打开文件.被写入的文件若不存在,则创建该文件.

  • 每写入一个字符,文件内部位置指针向后移动一个字节.

  • fputc函数有一个返回值,如写入成功则返回写入的字符,否则返回一个EOF.可用此来判断写入是否成功.

文件写入例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# include <stdio.h>
# include <stdlib.h>

int main(void)
{
FILE* fp;
char ch, filename[20];

printf("请输入您需要写入的文件名");
scanf("%s", filename);

if (!(fp = fopen(filename, "wt+")))
{
printf("无法打开文件\n");
exit(0); //终止程序 //需要先stdlib.h才能使用
}
printf("请输入你需要写入的句子: ");
ch = getchar();
ch = getchar();
while (ch != EOF) //ctrl + z
{
fputc(ch, fp);
ch = getchar();
}

fclose(fp);
}

/*
在VS中的输出结果是:
请输入您需要写入的文件名123
请输入你需要写入的句子: 567
567
^Z

D:\studyC\文件写入\x64\Debug\文件写入.exe (进程 3064)已退出,代码为 0。
按任意键关闭此窗口. . .

*/

fgetc()函数调用:

ch=fgetc(fp);

函数功能:
其意义是从打开的文件fp中读取一个字符并送入ch中

fgetc()函数使用说明

  • 在fgetc函数调用中,读取的文件必须是以读或读写方式打开的
  • 当文件内部有一个位置指针.用来指向文件的当前读写字节
  • 在文件打开时,该指针总是指向文件的第一个字节.使用fgetc函数后,该位置指针将向后移动一个字节.因此可连续多次使用fgetc函数,读取多个字符
  • 应注意文件指针和文件内部的位置指针不是一回事
  • 文件指针是指向整个文件的,需在程序中定义说明,只要不重新赋值,文件指针的值是不变的.
  • 文件内部的位置指针用以指示文件内部的当前读写位置,每读写一次,该指针均向后移动,它不需在程序中定义说明,而是由系统自动设置的.

补充:从一个文本文件顺序读入字符并在屏幕上显示出来:

1
2
3
4
5
6
7
8
ch = fgetc (fp);
while(ch! = EOF)
{
putchar(ch);
ch = fgetc(fp);
}


注意:EOF不是可输出字符,因此不能在屏幕上显示.由于字符的ASCII码不可能出现-1,因此EOF定义为-1是合适的,当读入的字符值等于-1时,表示读入的已不是正常的字符而是文件结束符.

补充:从一个二进制文件顺序读入字符:

1
2
3
4
while (!feof(fp)) //eof=end of file文件结束意思
{
ch = fgetc(fp);
}

注意:ANSIC提供一个feof()函数来判断文件是否真的结束.如果是文件结束,函数feof(fp)的值为1(真);否则为0(假).以上也适用于文本文件的读取