数据结构学习总结——预备知识

零、写在前面的话(为什么写这个系列):

我记得《暗时间》里面说,学习一个知识并不只是知道,而是能够在关键时刻从记忆中提取出来这个知识。

怎样才能做到?

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


你可能感兴趣的:(数据结构学习总结——预备知识)