初识malloc与realloc
malloc与realloc函数大部分人可能实在C语言中接触到,我们知道,这两个函数是动态的使用内存,那么这样的好处为
1.不需要预先分配存储空间
2.分配空间的大小可以根据程序扩大或缩小
3.有效的使用内存空间
定义:
void *malloc(size_t size)//即括号里一个参数,如果开辟整型4位数的空间,那么括号里应为(sizeof(int)*4);
void free(void *pointer);//括号的参数也是一个指针
这两个函数都在头文件stdlib.h中声明。
malloc的作用是在内存的动态存储区中分配一个长度为size的连续空间,参数为无符号整型数,返回值为一个指向它所分配的连续存储域的起始地址(即指针)
注意:如果未分配成功,就会返回一个NULL指针(所以在使用时应该保证非空,加一条语句判断即可)
如果初始空间不够使用时,那么realloc就派上用场了。
我们首先看realloc的定义
void *realloc(void *p,size_t new size);
realloc只要用于修改一个原先已经分配的内存块大小,可以使一块内存扩大或缩小。
注意:
参数中的*p为 你想重新为谁开辟空间的首地址,第二个参数为 原先已经有的空间加上你想增加的空间,比如,原先我开辟了4个整型的空间,我想再增加4个,此时得需要用realloc函数
即:
int *p=(int *)malloc(sizeof(int)*4);//原先分配的4个整型空间
int *q=(int *)realloc(p,sizeof(int)*(4+4));//这里用q来指向新开辟内存空间的首地址而不用p,后续说明原因,这是为了安全操作。
注意:如果realloc失败,原先内存不改变,不会释放也不会移动。
但是如果,我只分配了四个整型空间,用指针为第7个空间进行赋值操作会发生什么呢
看下面例子:
#include<stdio.h>
int main()
{
int *p;
p=(int *)malloc(sizeof(int)*4);
*(p+2)=3;
printf("%d\n",*(p+2));
*(p+6)=7;
printf("%d",*(p+6));
return 0;
}
可以明白,3的结果可以出来,那你认为能出来结果7的那个结果吗?
答案是可以的。
3
7
这是因为,malloc空间创建的是一块连续的内存,当指针指向前四个位置是正常的,但是如果你这一块连续内存空间之后还有空间时,就会出现7那个结果,这是不一定会一直能出来结果的,如果这块空间之后没有剩余空间或者被存储其他数据时,就会出现危险,所以不推荐使用。
那么说一下为什么重新定义指针*q来指向新开辟的内存空间呢?
我们知道,realloc一般是用于扩大空间来使用的,当缩小空间时会发生什么情况呢
例如
#include<stdio.h>
int main()
{
int *p,*q;
p=(int *)malloc(sizeof(int)*5);
*(p+4)=4;
printf("%d\n",*(p+4));
*(p+6)=7;
printf("%d\n",*(p+6));
q=(int *)realloc(p,sizeof(int)*2);
printf("%d",*(q+4));
return 0;
}
而它的结果为
4
7
11080968
当缩小空间时,看结果知道在第五个位置,即*(p+4)与*(q+4)结果不同。
这是因为
当p所指向的那个空间需要更改时,realloc会在合适的地方重新创建合适的空间,把malloc里的信息复制到realloc中,而malloc中第五个空间显然没有没有复制到realloc中,那是因为空间小了,复制不过来,造成数据丢失。
如果不是用q指针指向realloc函数呢,就用原先的p指针指向新的首地址,会出现什么情况
看代码:
#include<stdio.h>
int main()
{
int *p,*q;
p=(int *)malloc(sizeof(int)*5);
*(p+4)=4;
printf("%d\n",*(p+4));
*(p+6)=7;
printf("%d\n",*(p+6));
p=(int *)realloc(p,sizeof(int)*2);
printf("%d",*(p+4));
return 0;
}
而结果是
4
7
4
多重复执行几次发现不一定是上面结果,也有可能是:
4
7
8131848
为什么会出现这个不同结果呢
这是因为
当用p指针指向新的realloc空间的首地址时,(p+4)会出现歧义,你是在操作原先malloc函数p指针还是 realloc函数的p指针,如果是第一种情况那么之前赋值为4的结果就可以出来,如果后者,就是一个空间的地址,所以,为了避免出现问题,我们重新定义一个q指针指向realloc的空间地址,是最安全的。而原先malloc的空间会释放掉。
所以,建议重新定义指针来指向realloc函数的,尤其要修改数据时,更要小心。
那么在操作栈中,有一个地方需要我们注意
下面是入栈操作的函数
我们首先要判断栈顶指针是否溢出,如果栈满就要追加空间
注意第六行****@地方
Status Push (SqStack &S, SElemType e) {
if (S.top - S.base >= S.stacksize) {//栈满,追加存储空间
S.base = (ElemType *) realloc ( S.base,
(S.stacksize + STACKINCREMENT) * sizeof (ElemType));
if (!S.base) exit (OVERFLOW); //存储分配失败
S.top = S.base + S.stacksize;****@
S.stacksize += STACKINCREMENT;//更新空间大小
}
*S.top++ = e;放入栈中数据
return OK;
}
如果直接用S.top++可以吗
既然更新数据了,那么我直接让top指针加加,不就又重新指向下一个空间了吗,继续入栈就行吗
答案是不可以的,并且是极度危险的
代码中是追加存储空间的操作,里面是对*base即栈底指针进行赋值,指向realloc空间的首地址,而top指针并没有复制到新的空间区域中,也没有指向,那么,对原先的malloc内存中的top++,就又不止指向了什么地方,随意更改更会不小心出现大问题。
而用S.top指针重新赋值指向S.base+S.stacksize,再进行插入数据的操作,才是最安全的。