C语言内存分配:
静态内存:也就是栈空间内存,不用程序员自己分配。
1、栈区(stack):编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。栈里面的变量通常是局部变量、函数参数等。windows下,栈内存分配2M(确定的常数),超出了限制,提示stack overflow错误。自动分配,释放。
动态内存分配:由 malloc 等分配的内存块,和堆是十分相似的,不过它是用 free 来结束自己的生命的。
2、堆区(heap):由 new 分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个 new 就要对应一个 delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。堆可以动态地扩展和收缩。 程序员手动分配释放,操作系统80%内存。
3、全局区或静态区。
4、字符常量区。
5、程序代码区。
void main(){
//静态内存分配,在栈内存中
int a[100*1024*1024];
getchar();
}

会出现stack overflow错误,也就是栈溢出。
void main(){
//动态内存分配
int* p = malloc(1024*1024*sizeof(int));
//printf("%#x\n",p);
//释放
free(p);
getchar();
}
上面就是使用malloc动态分配sizeof(int)M内存,然后使用free释放分配的内存。
静态内存分配:分配固定大小的内存空间。 存在问题:1、容易超出栈内存最大值,也就是造成栈溢出。 2、为了防止内存不够用会开辟更多的空间,容易浪费内存。
动态内存分配:在程序运行过程中,动态指定需要使用的内存大小,手动释放,释放之后这些内存还可以被重新使用。
创建一个数组,动态指定数组的大小
void main(){
int size;
scanf("%d",&size);
//开辟内存,大小size*sizeof(int)字节
int* p = malloc(size*sizeof(int));
//p是数组的首地址,p就是数组的名称
//给数组元素赋值(使用这一块刚刚开辟出来的内存区域)
int i = 0;
for (; i < size; i++){
p[i] = rand() % 300;
printf("%d %#x\n", p[i], &p[i]);
}
//释放p
free(p);
getchar();
}
realloc :重新分配内存
void main(){
int size;
scanf("%d",&size);
//开辟内存,大小size*sizeof(int)字节
int* p = malloc(size*sizeof(int));
//p是数组的首地址,p就是数组的名称
//给数组元素赋值(使用这一块刚刚开辟出来的内存区域)
int i = 0;
for (; i < size; i++){
p[i] = rand() % 300;
printf("%d %#x\n", p[i], &p[i]);
}
int addSize;
printf("输入数组增加的长度:");
scanf("%d", &addSize);
//内存不够用,扩大刚刚分配的内存空间
int* p2 = realloc(p, (addSize + size)*sizeof(int));
if (p2 == NULL){
printf("重新分配失败");
}
//重新赋值
int j = 0;
printf("\n\n");
for (; j < (size + addSize); j++){
p2[j] = rand() % 100;
printf("%d %#x\n", p2[j], &p2[j]);
}
//手动释放内存
if (p != NULL){
free(p);
p = NULL;
}
if (p2 != NULL){
free(p2);
p2 = NULL;
}
getchar();
}

重新分配内存的两种情况: 1、缩小,缩小的那一部分数据会丢失 2、扩大,(连续的) (1)如果当前内存段后面有需要的内存空间,直接扩展这段内存空间,realloc返回原指针; (2)如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据库释放掉,返回新的内存地址; (3)如果申请失败,返回NULL,原来的指针仍然有效。
内存分配的几个注意细节: 1.不能多次释放 2.释放完之后(指针仍然有值),给指针置NULL,标志释放完成 3.内存泄露(p重新赋值之后,再free,并没有真正释放内存)