还先说说C语言的内存分配
1.栈区(stack):自动分配,释放内存,静态内存,就存于栈区
windows下,栈内存分配2M,超出了限制,会提示stack overflow的错误
2.堆区(heap):由开发人员分配释放,大概可以占用操作系统的80%
3.全局区或静态区
4.字符常量区
5.程序代码区
先来看一个静态分配的例子:
void main(){
int a[1024*1024*10] ;
system("pause");
}
这就是一个内存溢出的例子。我们通常声明一个变量,常规的写法,都是静态分配。 上面说了,这样分配,是自动管理内存。且在是栈内存上。有上限,是2M。 而上述的写法,达到了40M,因为int型为4字节。故这里就会报溢出。
再来看一个动态分配的例子:
void main(){
int len;
printf("第一次输入数组的长度:");
scanf("%d", &len);
int* p = malloc(len * sizeof(int));
int i = 0;
for (; i < len; i++){
p[i] = rand() % 100;
printf("%d,%#x\n", p[i], &p[i]);
}
free(p);
system("pause");
}
关键代码:int* p = malloc(len * sizeof(int));
这里的malloc,就是分配动态内存的代码,这个方法的返回值是 void* ,就是说,可以是任意类型的指针! 这个指针,指向的就是malloc生成的一块内存区域地址!!!!!例子中,实际上,是给一个int数组分本动态内存。 那么其指针p,其实就是第一个元素的地址而以,详情指针那节的内容。当使用完后,要手动释放内存 free(p);
那这个动态内存,能不能动态增加呀--当然是可以的
接上面的代码:
int* p2 = realloc(p, sizeof(int) * (len + addLen));
可以看出,realloc就是增加一块内存的大小!第一个参数,就是动态内存的指针, 第二个参数,就是增加后的总大小!
那新的这一块内存的内存地址,和旧的有没有地址相比,有没有改变?
就是说,p2和p的值,是否相等。
答案是,可能等,也可能不等。
如果重新分配后,是缩小,则肯定是同一块
若是扩大,就不一定了。就要看原来的内存区域,扩大后的线性空间有没有被占用,若被占用,则会重新开辟内存,则P2和P就不同! 若没被占用,则是同一块!
还有可能,没有已经内存可被扩大了!则返回NULL。原指针依然有效
因此,可以先判断 if(p2!=null){free(p2)}
除了malloc外,还有一种写法
int *p=calloc(len, sizeof(len));
唯一的区别就是, calloc的两个参数比malloc的更直观,更容易懂,其它都没差。就是callo定义了单位大小,且有多少单位! 不像malloc要自已去计算。
内存分配的几个注意事项
1.不能多次释放
2.释放完成后,给指针置NULL,标志已经释放内存
3内存泄漏(P重新赋值之后,再free,并没有真正释放内存)
第1点好理解,第2点的例子
if (p != NULL){
free(p);
p = NULL;
}
就是这样操作,因为可能有很多地方,都会先判断 p是否为NULL,以免第1条悲剧的发生。
第3条的例子
int* p = malloc(2 * sizeof(int));
p = malloc(5 * sizeof(int));
free(p);
这就是内存泄漏,因为再次赋值时,返回的p,与第一个p,不是同一个了! 这样就相当于,释放了第2个p,第一个p就泄漏了!
正确的写法是
int* p = malloc(2 * sizeof(int));
free(p);
p = malloc(5 * sizeof(int));
free(p);
就是说,在重新赋值前,就要先释放一次!
动态扩,用realloc就不存在这个问题。