目录
1.动态内存分配
顾名思义,动态内存分配是动态的,指在程序执行过程中动态地分配或者回收存储空间的分配内存的方法。
- 为什么要进行动态内存分配?
//我们通常进行内存开辟的方式如下
char arr[4]={0};
int a=5;
这种方式开辟空间大小是固定的,并且声明数组时,必须指定数组的长度,它所需要的内存在编译时分配。
但是我们有时对于空间的需求得在我们程序运行的时候才知道,那么数组在编译时开辟空间的方式就不能满足了。
2.动态内存函数
-
malloc
void* malloc (size_t size);//字节
malloc函数向空间申请一块连续可用的空间,返回这块指向这块空间的指针
- 如果申请成功,则返回一个指向开辟好空间的指针;
- 如果申请失败,则返回一个空指针;
- 返回值为void*,说明malloc函数未知名返回指针的类型,具体使用时用什么类型取决于使用者自己;
- 如果参数size为0(申请0字节的空间),具体如何取决于编译器。
举例:
int main() {
char* ptr = (char*)malloc(10);
//开辟了一块大小为10的连续空间,指针类型为char*---->(char ptr[10])
if (ptr!= NULL) {
//
}
free(ptr);
ptr = NULL;
}
-
calloc
void* calloc (size_t num, size_t size);
- 函数的功能为num个大小为size的元素开辟一块空间,并且把每一个元素初始化为0;
- 与malloc的区别在于:calloc会在返回地址前,把申请空间的每一个字节初始化为0。
举例:
int main()
{
char* ptr = (char*)malloc(10, sizeof(char));
if (ptr != NULL) {
//
}
free(ptr);
ptr = NULL;
return 0;
}
-
realloc
void* realloc (void* ptr, size_t size);
realloc函数可以做到对动态内存空间分配大小的调整。
- ptr为要调整的内存地址,size为调整之后的大小,返回值为调整之后的空间起始位置;
- realloc函数调整源空间大小的同时,还会将原来内存中的数据移动到新的空间;
realloc调整内存空间时存在两种情况:
1.原有空间之后有足够大的空间
此时,之间在原有内存之后直接追加空间,原来空间数据不变。
2.原有空间之后没有足够大的空间
此时,需要在堆空间上另外找一个合适大小的连续空间,函数返回的是一个新地址。
int main() {
char* ptr = (char*)malloc(10);//开辟了一块大小为10的连续空间,指针类型为char*---->(char ptr[10])
if (ptr != NULL) {
//
}
//ptr= (char*)realloc(ptr, 20);
//错误,因为开辟失败时,返回的是空指针,ptr被置空
//相当于弄丢空间起始地址,无法找到原先开辟的空间了
char* p = (char*)realloc(ptr, 20);
if (p != NULL) {
ptr = p;
p = NULL;
}
free(ptr);
ptr = NULL;
return 0;
}
注意:
int *p=(int *)malloc(20)<==>int *p=realloc(NULL,20)
-
free
void free (void* ptr);
free函数用来释放动态开辟内存的空间
- 如果ptr指向的空间不是动态内存开辟的,无效;
- 如果参数为NULL指针,则函数什么事都不做。
通过上述例子,我们发现,free函数与动态内存开辟函数是成双成对出现的 。
特别注意:free的参数一定得是开辟的动态内存空间的起始地址,free后释放了这块空间,内存被收回。但是请注意!!!free后一定要置空,否则,这个指针依然指向这块内存,即成为了“野指针”,后续程序可能会误以为这块这个指针合法,若是不小心调用了这个指针,则会导致非法访问,程序崩溃!
“野指针”:非法指针,指针指向的位置是不可知的(随机的,不正确的,未明确限制的)。
野指针的成因:
1.指针未初始化
int*p;//指针未初始化时,默认为随机值
*p=20;//错误
2.指针越界访问
当指针指向的范围超过数组的范围是,就是野指针
3.指针动态内存开辟
即我们上述说的free后不置空的例子。
3.常见动态内存错误
- 对空指针的解引用操作
int main()
{ int* ptr = (int*)malloc(20);
//错误:
*ptr = 18;//如果ptr的值NULL,就会出现问题(对空指针的解引用)
//正确:
if (ptr == NULL) {
perror("malloc")//打印错误
}
else{
*p=18;
}
printf("%d", *ptr);
return 0;
}
- 对动态开辟空间的越界访问
int main(){
int* ptr = (int*)malloc(20);
if (ptr == NULL) {
return 1;
}
for (int i = 0; i < 8; i++) {
*(ptr + i) = i;
//p[i]=i;
}//越界访问
free(ptr);
ptr = NULL;
return 0;
}
- 对同一块动态内存多次释放
int main(){
int* ptr = (int*)malloc(20);
if (ptr == NULL) {
return 1;
}
for (int i = 0; i < 5; i++) {
*(ptr + i) = i;
//p[i]=i;
}
free(ptr);
free(ptr);//多次释放
return 0;
}
- 对非动态开辟内存使用free释放
int a=20;
int *p=&a;
free(p);//错误
- 使用free释放动态开辟内存的一部分
int main(){
int* ptr = (int*)malloc(20);
if (ptr == NULL) {
return 1;
}
for (int i = 0; i < 5; i++) {
*(ptr++)=i;
}
free(ptr);//此时ptr不是这块空间的起始地址了
ptr = NULL;
return 0;
}
总结
祝大家健健康康,顺顺利利!!!下次见~