零、写在前面的话(为什么写这个系列):
我记得《暗时间》里面说,学习一个知识并不只是知道,而是能够在关键时刻从记忆中提取出来这个知识。
怎样才能做到?
1、必须要把这个知识进行推理。推理这个过程,把别人的知识,变成了自己的知识。
2、增加很多条提取线索。这个怎么做到?那本书作者说:“每天重复重复就可以了。但是我估计大家都做不到,所以有一个捷径:总结+讲给一个完全不懂的人听。那么写博客就是一个很好的方式,又可以总结,又可以讲给别人听。”
所以这就是我写这个系列的原因,学习数据结构的一个历程。尤其我发现写的过程中,很多东西都变得连贯,以前不懂的点突然就开朗了。所以应该会继续写下去吧。
顺便说最近看数据结构真是心好累
一、指针
1、指针是什么?
指针就是指向某个地址的一个变量。地址就是:内存单元的编号。
2、指针的类型和指针指向的类型
这个问题很容易混淆,很多人就是因为这个搞不懂指针。比如说:int *a,定义了一个指针a。那么它指针的类型是什么?很简单,就是把变量名去掉的定义语句的部分,这个例子的指针的类型就是int *。那它指针指向的类型是什么?就是这个指针指向的变量的类型,也就是把变量名和星号去掉的定义语句部分,这个例子中指针指向的类型就 是,int。
又比如二级指针 int **b;
它的指针的类型就是 int **
指针指向的类型就是 int *
3、指针的大小
地址的大小为4个字节,所以指针都是4个字节大。所有指针都是4个字节大,不管是指向double、char。
为什么不管指向什么类型都是4个字节?
举个例子,double b,*a;a=&b;
b是double类型的,所以b占8个字节,一个字节一个地址,有8个地址。指针a指向b,a只是指向b中的首个字节或者末一个字节的地址。而double*告诉我们这个地址之 后或者之前还有多少个地址属于指向的变量。
4、为什么重要?
(1)改变实参的值
我们从头开始讲:函数的工作方式是把实参复制一份副本到函数里面操作,所以就算在函数里面改变了实参的值,其实改变的是实参的副本的值。
那我们怎么用函数改变实参的值?
既然是复制一份副本到函数里,我们把实参的值复制到函数就没用了。所以,我们可以用取址符把实参的地址发到函数里,然后通过指针指向,来改变实参的值。
(2)节省内存
比如说我们定义了一个结构体(什么是结构体下面会说到,这里不需要太明白只知道它可能很大就可以),比如说有200个字节。如果我们直接把这个结构体传递给函 数,就要额外的200字节的内存。而如果我们只传递一个地址,占多大内存?地址的内存多大?对了,就是4个字节。这就省了196个字节。是一个比较好的方式。
二、结构体
1、什么是结构体?
根据自己需要创建的一种复合数据类型
2、为什么需要结构体?
简单的数据类型不能满足需要
3、怎样用?
(1)建立
举例子说,我们建一个叫做student的数据类型。那我们说怎样确定一个学生?学号、姓名,对吧。所以,我们可以这样建。
struct student //建立一个名叫student的结构体
{
int sid; //学号
char name[200]; //姓名
}; //注意大括号后面有一个分号,不能省!
(2)使用
就像其他基本数据类型一样,比如说 int a;//定义一个名为a的int型变量。
类似的: struct student b;//定义一个名为b的struct student型变量,注意前面的struct也要有。
那么这样做是不是很麻烦,我们有一种稍微简便的方法。
(3)typedef
这个就相当于一种替换关系把,我理解的。用法也很简单
拿上面的学生结构体来举例:
typedef struct student
{
int sid;
char name[200];
}STUDENT;
以后我们就可以把STUDENT当作一个类型,简便一点。就像这样:STUDENT b;这样做和上面的struct student b;是等价的。
还有一种用法,是直接定义这个类型的指针:
typedef struct student
{
int sid;
char name[200];
}STUDENT,*PSTUDENT;
这样如果我们想定义一个struct student *类型的指针可以直接用PSTUDENT来定义。
例如:struct student *b;与PSTUDENT b;是等价的。
不过我不太喜欢这样用,如果只是一级指针还好,如果是二级三级就有些乱了。直接用SUDENT * 就可以了,这样又简便又明朗,是一种比较好的写作风格吧(我猜)。
(4)怎样使用结构体内的某个数据项呢
两种方法:又用上面的STUDENT举例,先定义一个STUDENT变量:STUDENT st;
一种方法可以直接用变量名:st.sid=XXX;
另一种方法就是用指针: STUDENT *pst=&st;
pst->sid=XXX;//理解为pst指针指向的那个STUDENT结构体变量中的sid数据项的值被赋为XXX。
上面注释中的话很重要,我一开始没注意。到后面实现链表的时候就乱套了,又回去找这句话,反反复复的读直到印到脑子里(成钢印族了= =)。
真的很重要。
(5)赋值问题
结构体赋值有两种方式:整体或者单独赋值。
用上面的STUDENT结构体来说, 整体的可以这样赋值: STUDENT st={0121220390100,"Zhangsan"}。没有问题。
但是单独赋值的时候要注意,不!能!这!样!:st.name="Zhangsan";字符串不是这么赋给数组的,要用strcpy函数。
三、分配动态内存
1、为什么要分配动态内存?
又要从头说:我们说一个函数执行完后,分配给它的内存还存不存在?是这样的,内存分为两部分:静态内存和动态内存。
静态内存在栈中分配,这部分内存是系统要收回的。比如说 : int i; f(int j).里面的i和j都是存在静态内存。
而动态内存在堆中分配,如果不显式的释放,是不会被收回的。当然整个程序结束后,是会被收回的。所以为什么windows的系统越来越慢?就是因为动态内存的存在,windows是用c编的,如果不显式释放这些内存,就泄露了。
那么如果我们在函数中获得了一些内存,而又希望在函数外部使用怎么办?就用动态内存。
2、怎样分配?
举个例子:int *pArr=(int *)malloc (sizeof(int)*len);//实际上就是创建一个len长度的数组
malloc函数的作用就是分配一些动态内存给函数,而且会返回这部分内存的第一个字节的地址。
那么为什么malloc前面要加(int *)?
我们上面讲指针时就说了,指针是指向第一个字节的地址,不论指向的变量有多少字节。但是光指向是无意义的,必须用类型来确定这个字节之后多少个字节也是属于 这个变量的。
sizof函数就不说了,就是测一个类型占多少个字节。
3、怎样使用分配的内存?
由于分配的动态内存是连续的。所以我们有两种方法可以使用:*pArr 是可以使用的,pArr[0]也是可以的。
为什么?因为pArr[i]等价于*(pArr+i)
4、怎样释放动态内存?
使用free函数来释放。
free(pArr);//释放pArr指向的内存
注意free后面的括号填的是指针。
5、头文件
注意动态分配内存和释放都需要用到 malloc.h 头函数。
2015年9月26日 21:17:11