动态内存分配(十)


动态内存分配(十)

在不知道所需要的空间大小的情况下,这时就可以使用动态内存开辟。
当开辟的空间不再使用时,用free函数来释放calloc、malloc或realloc所分配的内存空间。

动态内存函数

free函数

free函数是用来释放动态开辟的空间
格式:
void free(void *ptr)
free函数的头文件: stdlib.h

malloc函数

malloc函数会在内存开辟一块连续可用的空间,并返回一个指向它的指针,若开辟失败,则返回NULL,所以malloc的返回值一定要检查。
格式:
void *malloc(size_t size)
头文件: stdlib.h
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
int i=0;
//向内存申请10个整形空间
//因为malloc的返回值的类型是void*,所以需要用(int*)将其转换化为int*类型
int* p=(int*)malloc(10*sizeof(int));
//检查开辟成功还是失败
if(p==NULL)//判断指针是否为空,为空则代表开辟空间失败
{
//打印错误原因
printf("%s\n",strerror(errno));
}
else
{
//赋值并打印
for(i=0;i<10;i++)
{
*(p+i)=i;
}
for(i=0;i<10;i++)
{
printf("%d ",*(p+i));//0 1 2 3 4 5 6 7 8 9
}
}
//释放空间
free(p);
//此时,虽然空间被释放掉,但p依然指向那个地址,所以需要将p赋一个空指针
p=NULL;
return 0;
}

calloc函数

calloc函数会在内存开辟一块连续可用的空间,并返回一个指向它的指针。
与malloc的不同是,calloc会把开辟的空间的每个字节初始化为零。
格式
void *calloc(size_t nitems, size_t size)
nitems:元素个数。
size:元素大小。
头文件: stdlib.h
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
int i=0;
int* p=(int*)calloc(10,sizeof(int));
//向内存申请10个大小为int的空间并将每个字节初始化为0
if(p==NULL)
{
printf("%s\n",strerror(errno));
}
else
{
for(i=0;i<10;i++)
{
printf("%d ",*(p+i));//0 0 0 0 0 0 0 0 0 0
}
}
free(p);
p=NULL;
return 0;
}

realloc函数

realloc函数可以调整动态开辟空间的大小。
使用realloc函数的注意事项:

  1. 原有空间后有足够大的空间,就直接在原有内存后追加空间,原来空间内容不发生变化。
  2. 原有空间后没有足够大的空间,则另找一个合适大小的连续空间来使用,并将原内存的数据移动到新的空间,释放旧的内存,函数返回的将是一个新空间的地址。
  3. 需要用一个新的指针变量来接受realloc函数的返回值。

格式:
void realloc(void *ptr, size_t size)
*
ptr:之前开辟的内存块地址
**size
:调整后新大小
例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
int i=0;
int* p=(int*)malloc(20);
//使用malloc开辟了20个字节空间
int* p1=NULL;
if(p==NULL)
{
printf("%s\n",strerror(errno));
}
else
{
for(i=0;i<5;i++)
{
*(p+i)=i;
}
}
//希望有40个字节空间
p1=(int*)realloc(p,40);
//使用realloc调整内存空间为40个字节
if(p1!=NULL)
{
p=p1;
for(i=5;i<10;i++)
{
*(p+i)=i;
}
for(i=0;i<10;i++)
{
printf("%d ",*(p+i));//0 1 2 3 4 5 6 7 8 9
}
}
//释放内存
free(p);
p=NULL;
return 0;
}

常见的动态内存错误

1.对NULL指针解引用操作
错误示例:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p=(int*)malloc(40);
//有可能malloc开辟空间失败返回的NULL
*p=10;
return 0;
}

2.对动态开辟空间的越界访问
错误示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i=0;
int* p=(int*)malloc(10*sizeof(int));//10个int
if(p==NULL)
{
return 0;
}
for(i=0;i<=10;i++)//越界
{
*(p+i)=i;
}
free(p);
p=NULL;
return 0;
}

3.对非动态开辟的内存使用free释放
错误示例:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdilb.h>
int main()
{
int a=10;
int* p=&a;
free(p);//对非动态开辟的内存使用free释放
p=NULL;
return 0;
}

4.使用free函数释放动态开辟内存的一部分
错误示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i=0;
int* p=(int*)malloc(10*sizeof(int));
if(p==NULL)
{
return 0;
}
for(i=0;i<10;i++)
{
*p++=i;
}
//p已经不在原来的位置了
free(p);
p=NULL;
return 0;
}

5.对同一块动态内存多次释放
错误示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i=0;
int* p=(int*)malloc(10*sizeof(int));
if(p==NULL)
{
return 0;
}
free(p);
free(p);
return 0;
}

6.忘记释放动态开辟内存
错误示例:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdlib.h>
int main()
{
while(1)
{
malloc(1);
}
return 0;
}

柔性数组(C99)

在结构体内,结构体中的最后一个元素可以是未知大小的数组,而且有特定的形式[]或者[0],叫做柔型数组成员

  • 结构体中柔性数组成员前面必须至少有一个成员
  • 在计算结构体大小时,不包含柔性数组成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <stdlib.h>
struct S
{
int n;
int arr[];
};
int main()
{
int i=0;
struct S* p=(struct S*)malloc(sizeof(struct S)+5*sizeof(int));
struct S* p1=NULL;
p->n=1;
if(p==NULL)
{
return 0;
}
for(i=0;i<5;i++)
{
p->arr[i]=i;
}
p1=(struct S*)realloc(p,44);
if(p1!=NULL)
{
p=p1;
for(i=5;i<10;i++)
{
p->arr[i]=i;
}
for(i=0;i<10;i++)
{
printf("%d\n",p->arr[i]);
}
}
free(p);
p=NULL;
return 0;
}

C语言内存分区

1.栈区(stack)

栈区是一种先进后出的内存结构,由编译器自动分配释放,存放函数的返回值、局部变量、函数参数、返回地址等。

2.堆区(heap)

用于动态内存分配。一般由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。

3.数据段(静态区)(static)

存放全局变量,静态数据,程序结束时由操作系统释放。

4.代码段

存放函数体的二进制代码。


Author: ljs
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source ljs !
评论
  TOC