阅读:0

函数间传递动态内存,C语言函数间动态内存的传递详解

跨函数使用动态内存很重要。所谓“跨函数使用动态内存”就是指“如何在主调函数中使用被调函数中动态分配的内存”。前面章节我们介绍了指针,其目的一是为了讲“动态内存分配”。第二个目的就是为了讲“跨函数使用动态内存”。

下面来写一个程序:
# include <stdio.h>
# include <stdlib.h>
void DynamicArray(int **q);  //函数声明
int main(void)
{
    int *p = NULL;
    DynamicArray(&p);  //函数调用
    printf("*p = %dn", *p);
    return 0;
}
void DynamicArray(int **q)  //DynamicArray是“动态数组的意思”
{
    *q = malloc(sizeof*q);
    **q = 5;
    return;
}
输出结果是:
*p = 5

程序说明:
1) “int*p;”表示定义了一个 int* 型的指针变量 p,它只能指向 int 型变量,里面只能存放 int 型变量的地址,但此时它里面还没有内容,也就是说还没有初始化。那么 p 是什么时候被初始化的?当调用完 DynamicArray 函数后,DynamicArray 函数构建了一个动态的内存空间,且 p 指向了这个内存空间,此时 p 才被初始化。

2) p 虽然是指针变量,但指针变量也是变量,只要是变量,在程序执行时系统就会为其分配内存单元,所以 p 也有自己的地址。系统为 p 分配内存单元是自动的,而给 p 初始化却是程序员的事。所以不要把“p 的地址”和“p 里面存放的别的变量的地址”给弄糊涂了。

3) 函数调用时为什么传递的是 &p 而不是 p?我们可以试一试把实参改成 p,看看会怎么样:
# include <stdio.h>
# include <stdlib.h>
void DynamicArray(int *q);  //函数声明
int main(void)
{
    int i = 2;
    int *p = &i;
    DynamicArray(p);  //函数调用
    printf("*p = %dn", *p);
    return 0;
}
void DynamicArray(int *q)
{
    q = malloc(sizeof*q);
    *q = 5;
    return;
}
输出结果是:
*p = 2

首先,实参改成了 p,那么形参就不能再写 int**q 了。因为 p 是 int* 型,所以 q 也必须是 int* 型。同理,“*q=malloc(sizeof*q);”中赋值号左边的 q 前面的 * 也要掉。“**q=5;”也要改成“*q=5;”。

此时指针变量 p 指向变量 i,那么指针变量 q 也指向变量 i,即 q 中存放i的地址。但随后构建了一个动态内存,且 q 指向这个内存。q 中原本存放的i的地址被新的地址取代了,此时 q 不再指向 i,所以 *q 中的值的改变不会影响 i 的值。所以程序最后执行的结果 *p 还是等于 2。

这个实际上同前面讲的普通变量的传递是一样的。对于普通变量,如果想在被调函数中直接修改主调函数中变量的值,那么就必须要传递该变量的地址。对于指针变量也是一样的,如果想在被调函数中修改主调函数中定义的指针变量的指向,那么也要传递该指针变量的地址。因为修改指针变量的指向就是修改指针变量的值。

4) 为什么形参是 int**q,而不是 int*q?同样可以用两种方式理解:
  1. 因为实参传递的是“指针变量的地址”,而指针变量的地址是指针的指针,基类型为int*型,所以形参的基类型也必须是int*型才能进行传递。
  2. 因为指针变量p的类型是int*型,所以&p的类型为int**型,所以形参也必须是int**型才能进行传递。

5) 从该程序中也可以看出,动态分配的内存在函数调用结束后并没有被释放。通过输出结果可以看出,它里面存放的仍然是 5。这就是动态内存分配。

多级指针就是比较“绕”,难度其实不是很大。到底哪个存放的是哪个的地址,这个如果你一开始想不清楚的话可以用笔在纸上画一下。

最后需要讲的是,“跨函数使用动态内存”也可以不用多级指针,即直接返回被调函数中指向动态内存的指针变量,然后赋给主调函数中的指针变量就行了。下面把函数写下来。
# include <stdio.h>
# include <stdlib.h>
int * DynamicArray(void) ;  //函数声明
int main(void)
{
    int *p = DynamicArray();  //函数调用
    printf("*p = %dn", *p);
    return 0;
}
int * DynamicArray(void)  //DynamicArray是“动态数组的意思”
{
    int *q = malloc(sizeof*q);
    *q = 5;
    return q;
}
输出结果是:
*p = 5

这种方式相比前面使用多级指针的方式更好理解。而且在实际编程中,多是使用这种方式。但是需要注意的是,只有动态分配的内存空间的地址才能返回。这个程序中指针变量 q 所指向的内存空间是用 malloc 定义的,即动态分配的。所以函数调用结束后这段内存空间也不会被释放,因此返回它的地址才有意义。若 q 所指向的内存空间不是动态分配,而是在栈中静态分配的,那么就不能返回它的地址。因为函数调用结束后这段内存空间已经被释放了,不能使用,所以返回去也没有意义。

因此,在 C 语言中,在讲动态内存分配之前经常有一句话,叫作“永远不要返回局部变量的地址”。