@Chita_
C语言 动态分配内存
- 动态内存管理
- callco
- free
- malloc
- realloc
- 动态内存的常见问题
重述:指针的用法
1)提供访问存储在数组中的信息的另一种方法
2)提供另一种(更有效的)向函数传递参数的方法
3)启用动态数据结构,这些数据结构是在运行时从堆中分配的内存块构建的
为什么要动态分配内存?
- 可能不能提前知道存储数据所需的变量(如数组)空间
- 与静态分配相比:
- 如果预定义的大小很小,它可能没有足够的空间来容纳数据,从而导致程序失败
- 如果预先设定的大小较大,大部分空间将不会被利用,造成浪费或效率低下
动态分配内存
- 允许程序在执行过程中动态地为一些变量(如数组)分配内存
- 方法:
- 程序有允许用户请求一定量内存的例程
- 然后用户使用这个内存,并在它们完成时返回它
- 在堆段中分配内存
动态内存管理的函数
- calloc - - - 分配内存数组
- malloc - - - 分配单个内存块
- realloc - - - 扩展或减少先前分配的空间量
- free - - - 释放一块不再需要的内存
动态分配的内存不会在函数结束时消失,必须显式地释放它
calloc - 分配内存数组
void *calloc(size_t num, size_t esize);
- size_t : 用于指示大小的特殊类型, unsigned int
- num : 要在数组中分配的元素数
- esize: 要分配单个元素的大小(以字节为单位)
- 以获得正确的值,使用sizeof(< type >)
- 分配的内存大小为num*esize - calloc返回该内存的第一个字节的地址
- 将返回的地址转换为适当的类型 - 如果没有足够的可用内存,calloc返回NULL
float *nums;
int a_size;
int idx;
printf("Read how many numbers:");
scanf("%d",&a_size);
nums = (float *)calloc(a_size, sizeof(float));
/* nums is now an array of floats of size a_size */
for (idx = 0; idx < a_size; idx++) {
printf("Please enter number %d: ",idx+1);
scanf("%f", nums+idx); /* read in the floats */
}
/* Calculate average, etc. */
潜在的问题在哪里?
- nums+idx是地址, 后续输出时应输出nums[idx]
始终检查calloc,malloc或realloc的返回值!
float *nums;
int a_size;
int idx;
printf("Read how many numbers:");
scanf("%d",&a_size);
nums = (float *) calloc(a_size, sizeof(float));
if(nums == NULL) {
/* exit or do some other stuff */
}
…
free - 将内存返回堆
void free(void *ptr);
- 由ptr指向的位置的内存被释放(以便可以再次使用)
- 程序跟踪由内存开始位置分配的每个内存
- 如果释放一块分配有calloc的内存,则将释放(释放)整个数组
- 未定义行为,如果我们作为地址传递以释放未动态分配(或已经释放)的地址
float *nums;
int a_size;
printf("Read how many numbers:");
scanf("%d",&a_size);
nums = (float *) calloc(a_size, sizeof(float));
/* Use array nums */
…
/* When done with nums: */
free(nums);
/* Would be an error to do it again - free(nums) */
malloc - 分配内存
void *malloc(size_t esize);
- 类似于calloc,不同的是我们使用它来分配给定大小的单个块
- 如果内存不足,返回NULL
- 如果不再需要内存,则必须使用free释放内存
- 以下是等价的:
malloc(a_size*sizeof(float));
<==>calloc(a_size, sizeof(float));
float *nums;
int a_size;
int idx;
printf("Read how many numbers:");
scanf("%d",&a_size);
nums = (float *) malloc(a_size * sizeof(float));
if(nums == NULL) {
/* exit or do some other stuff */
}
…
realloc - 增加/减少内存分配
void *realloc(void *ptr, size_t esize);
- ptr是一个指向先前动态分配的一块内存的指针
- size是要分配的新大小
- 如果重新分配失败,返回NULL
- 函数执行以下操作:
1) 分配size大小的内存
2) 将ptr内存中的内容复制到新内存的第一部分
3) —释放旧的内存块
4) 返回到新内存块的地址
float *nums;
int a_size;
nums = (float *)calloc(5, sizeof(float));
/* nums is an array of 5 floating point values */
for (a_size = 0; a_size < 5; a_size++)
nums[a_size] = 2.0 * a_size;
/* nums[0]=0.0, nums[1]=2.0, nums[2]=4.0, etc. */
nums = (float *)realloc(nums, 10*sizeof(float));
/* An array of 10 floating point values is allocated, the
first 5 floats from the old nums are copied as the first 5
floats of the new nums, then the old nums is released */
分配二维数组的内存
- 不能简单地动态分配二维及多维数组
- 解决
1)分配一个指针数组(一维)
2)使每个指针指向一个大小合适的一维数组
float **A; /* A is an array (pointer) of float pointers */
int X;
A = (float **) calloc(5, sizeof(float *));
/* A is a 1D array (size 5) of float pointers */
for (X = 0; X < 5; X++)
A[X] = (float *) calloc(4, sizeof(float));
/* Each element of array points to an array of 4 float
variables */
/* A[X][Y] is the Yth entry in the array that the Xth member of A
points to */
不规则尺寸的2D阵列
float **A;
int X;
A = (float **)calloc(5, sizeof(float *));
for (X = 0; X < 5; X++)
A[X] = (float *) calloc(X+1, sizeof(float));
动态内存的常见问题
问题1
返回一个指向自动变量的指针
int foo(void)
{
int x;
…
return &x;
/ x does not exist outside the function /
/ Returning its address will result in unknown behaviour */
}
问题2
堆块溢出:类似于数组越界
void foo(void)
{
int *x = (int ) malloc(10 * sizeof(int));
x[10] = 10;
/ Allocated memory is only up to x[9] */
…
free(x);
}
问题3
内存泄漏:丢失指向已分配内存的指针
int pi;
void foo(void)
{
pi = (int) malloc(8sizeof(int));
/ Leaked the old memory pointed to by pi /
…
free(pi); / foo() is done with pi, so free it /
}
int main(void)
{
pi = (int) malloc(4*sizeof(int));
foo();
}
问题4
- 潜在的内存泄漏
- 内存块开始的指针丢失
- 仍然可以通过指针算术恢复
int *ip = NULL;
void foo(void)
{
ip = (int ) malloc(2 * sizeof(int));
…
ip++;
/ ip is not pointing to the start of the block anymore */
}
问题5
释放非堆或未分配的内存
void foo(void)
{
int fnh = 0;
free(&fnh); /* Freeing stack memory */
}
void bar(void)
{
int *fum = (int ) malloc(4 * sizeof(int));
free(fum+1); / fum+1 points to middle of block /
free(fum);
free(fum); / Freeing already freed memory */
}