目录
0x00动态内存分配一般用在什么地方?
数组必须规定大小,一定得等到程序结束,才能释放内存。
为了防止内存浪费,我们一般都要使用动态内存分配。动态内存分配能够及时的把开出的内存释放掉,而不是等到程序结束才去释放。因为一般的大型程序都是7x24小时运行的,如果我们总是使用数组,就会导致我们的程序开了很多内存都没有释放,这样可能导致内存不够用。如果不使用动态内存分配,那么即便你内存有几个T,大型程序也会开完的,开完之后,你只能重启程序了,但是有很多程序,例如淘宝的服务器开了就不能停的。所以一定要学会用动态内存分配来节约内存。
注意:除非程序结束或者手动释放,否则new和malloc开的内存一直被占用。
0x01动态内存分配实现动态的数据结构
动态数组:数组大小是变化的。
如何实现动态数组?
由一个指针变量+一个记录数组元素个数的变量来实现动态数组。
有了动态数组之后,我们就可以用动态数组来实现队列和栈了。
栈:特殊的动态数组,只能够先入后出,后入先出。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
//实现动态的数组,我们只需要先用4字节开一个指向int类型的指针,先让它指向空,然后开内存段,让指针指向内存段
int* p=NULL;
//记录数组中元素的个数
int len =0;
//增加一个元素
void add(int data);
//删除
void pop();
//遍历
void travel();
//获取栈顶元素
int getTop();
int main(int argc, char *argv[]) {
add(1);
add(2);
add(3);
pop();
travel();
return 0;
}
void pop(){
if(len==1){
free(p);
p=NULL;
len--;
}
else if(len>1){
int* pNew=(int*)malloc((len-1)*sizeof(int));
memcpy(pNew,p,(len-1)*sizeof(int));
free(p);
p=pNew;
len--;
}
return;
}
int getTop(){
if(len){
return p[len-1];
}
return -1;
}
void add(int data){
//如果本来没有数据
int* pNew;
if(p==NULL){
//需要先开一个内存段,然后将数据放到该内存段里面去
pNew=(int*)malloc((len+1)*sizeof(int));//开内存段,然后将返回的指针强制转化为int*类型
}
else
//如果本来有数据
{
pNew=(int*)malloc((len+1)*sizeof(int));
//拷贝原有数据
memcpy(pNew,p,len*sizeof(int));//(目的地首地址,原有内存首地址,拷贝多少字节)
//释放原有内存
free(p); //注意这个代表释放p指向的内存段,而不是释放p的所占据的内存
}
pNew[len++]=data;
p=pNew; //p指向新开内存
}
void travel(){
int i;
for(i=0;i<len;i++){
printf("%d \n",p[i]);
}
}
用动态内存分配实现的动态数组不会浪费任何内存,如果数组里面没有存数据,就不会占用任何内存,数组里面存了多少数据就开多少内存。而普通的数组可能开辟很大内存,却没有存多少数据。
栈:先入后出,后入先出;可以理解为箱子。
队列:先入先出,后入后出;可以理解为日常生活中的排队。
空间复杂度:程序占用内存的多少
时间复杂度:程序运行时间长短。CPU运算次数的多少
用栈实现的动态数组,空间复杂度很低,但是比较耗费CPU,因为每一次放数据进去都要新开内存,还要拷贝。
除了考虑时间复杂度和空间复杂度,我们使用数据结构就是为了增删改查,那么用这种数据结构,我们增删改查容易吗?
0x02作业:写一个队列
注意:内存段释放了,内存段依然存在,占据内存,只是这个程序绑定了这一块内存段,其他程序不能访问该内存段,而释放内存段,是指解除绑定,其他程序也可以访问这块内存段了。不论什么数据结构,它的本质都是对内存段的占用。栈和队列都是用连续内存段来制作的,所以我们可以用一个指针变量来描述一个栈和一个队列。我们只需要知道元素个数和首地址,我们就知道这个队列或者栈了。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int* p=NULL;
//记录数组中元素的个数
int len =0;
//增加一个元素
void add(int data);
//删除
void reduce();
//遍历
void travel();
int main(int argc, char *argv[]){
add(1);
add(2);
add(3);
add(4);
add(8);
reduce();
reduce();
travel();
return 0;
}
void reduce(){
if(len==1){
free(p);
p=NULL;
len--;
}
else if(len>1){
int* pNew=(int*)malloc((len-1)*sizeof(int));
memcpy(pNew,p+1,(len-1)*sizeof(int));
free(p);
p=pNew;
len--;
}
return;
}
void add(int data){
//如果本来没有数据
int* pNew;
if(p==NULL){
//需要先开一个内存段,然后将数据放到该内存段里面去
pNew=(int*)malloc((len+1)*sizeof(int));//开内存段,然后将返回的指针强制转化为int*类型
}
else
//如果本来有数据
{
pNew=(int*)malloc((len+1)*sizeof(int));
//拷贝原有数据
memcpy(pNew,p,len*sizeof(int));//(目的地首地址,原有内存首地址,拷贝多少字节)
//释放原有内存
free(p); //注意这个代表释放p指向的内存段,而不是释放p的所占据的内存
}
pNew[len++]=data;
p=pNew; //p指向新开内存
}
void travel(){
int i;
for(i=0;i<len;i++){
printf("%d \n",p[i]);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/***************************************************************
使用动态内存分配的前提,这个程序是7*24小时连续不断运行的。 *
如果这个程序不是7*24小时连续不断运行的,我们不需要动态内存分配 *
****************************************************************/
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
//队列
//初始化队列
void initQueue(int** queue,int* pLen);//int** queque表示指向“指向int类型的指针”的指针
//入队
void pushQueue(int** queue,int* pLen,int data);
//遍历函数
void travel(int** queue,int* pLen);
//获得队首元素
int getHead(int*queue);
//返回队尾元素
int getTail(int*queue,int Len);
//修改某个元素值
void setOneOfQueue(int** queue,int idx,int len,int data);
//出队
void pop(int** queue,int* pLen){
if(*queue){//队伍非空才出队
if(*pLen == 1){
free(*queue);
*queue=NULL;
*pLen=0; //容易忘记
return;
}
int* pNew=(int*)malloc(sizeof(int)*((*pLen)-1));
memcpy(pNew,(*queue)+1,sizeof(int)*((*pLen)-1));
free(*queue);
*queue=pNew;
(*pLen)--;
return;
}
}
int main(int argc, char *argv[]){
int* pBuff;
int len;
printf("pBuff:%d,len:%d",pBuff,len);
initQueue(&pBuff,&len);//因为只有模拟按引用传参,才能通过函数修改pbuff和len;否则修改的只是副本
printf("pBuff:%d,len:%d",pBuff,len);
printf("\n");
pushQueue(&pBuff,&len,0);
pushQueue(&pBuff,&len,1);
pushQueue(&pBuff,&len,2);
pushQueue(&pBuff,&len,3);
pushQueue(&pBuff,&len,4);
pushQueue(&pBuff,&len,5);
travel(&pBuff,&len);
setOneOfQueue(&pBuff,1,len,1111);
travel(&pBuff,&len);
//void pop(int** queue,int* pLen)
pop(&pBuff,&len);
travel(&pBuff,&len);
printf("head:%d,tail:%d\n",getHead(pBuff),getTail(pBuff,len));
return 0;
}
void initQueue(int** queue,int* pLen){
*queue=NULL;
*pLen=0;
}
//入队函数
void pushQueue(int** queue,int* pLen,int data){
//新开内存
int* pNew=(int*)malloc(sizeof(int)*(*pLen+1));
//判断原来是否有数据
if(*queue){//有数据,拷贝原有数据
memcpy(pNew,*queue,sizeof(int)*(*pLen+1));
free(*queue);
pNew[(*pLen)++]=data;
*queue=pNew;
}
else{//没有数据
pNew[(*pLen)++]=data;
*queue=pNew;
}
}
//遍历函数
void travel(int** queue,int* pLen){
int i;
for(i=0;i<(*pLen);i++){
printf("%d\n",(*queue)[i]);
}
}
//获得队首元素
int getHead(int*queue){
if(queue) return queue[0];
return -1;
}
//返回队尾元素
int getTail(int*queue,int Len){
if(queue) return queue[Len-1];
return -1;
}
//修改某个元素值
void setOneOfQueue(int** queue,int idx,int len,int data){
if(!(*queue)) return;//队伍为空
if(idx<0 || idx>(len-1)) return;//下标不对
(*queue)[idx]=data;
}