C传递指针的指针

将指针传递给函数时,传递的是值。如果我们想修改原指针而不是指针的副本,就需要传递指针的指针。在下例中,我们传递了一个整数数组的指针,为该数组分配内存并将其初始化。函数会用第一个参数返回分配的内存。在函数中,我们先分配内存,然后初始化。所分配的内存地址应该被赋给一个整数指针。为了在调用函数中修改这个指针,我们需要传入指针的地址。所以,参数被声明为int指针的指针。在调用函数中,我们需要传递指针的地址:

void allocateArray(int **arr, int size, int value) {
    *arr = (int*)malloc(size * sizeof(int));
    if(*arr != NULL) {
        for(int i=0; i<size; i++) {
            *(*arr+i) = value;
        }
    }
}

这个函数可以用下面的代码测试:

int *vector = NULL;
allocateArray(&vector,5,45);

allocateArray的第一个参数以整数指针的指针的形式传递。当我们调用这个函数时,需要传递这种类型的值。这是通过传递vector地址做到的。malloc返回的地址被赋给arr。解引整数指针的指针得到的是整数指针。因为这是vector的地址,所以我们修改了vector

内存分配说明如图3-7所示。左图显示malloc返回且初始化数组后的栈状态。类似地,右图显示函数返回后的栈状态。

enter image description here

图3-7:传递指针的指针

注意 要方便地发现内存泄漏这样的问题,只需画一张内存分配图。

下面这个版本的函数说明了为什么只传递一个指针不会起作用:

void allocateArray(int *arr, int size, int value) {
    arr = (int*)malloc(size * sizeof(int));
    if(arr != NULL) {
        for(int i=0; i<size; i++) {
            arr[i] = value;
        }
    }
}

下面的代码段说明了如何使用这个函数:

int *vector = NULL;
allocateArray(vector,5,45);
printf("%p\n",vector);

运行后会看到程序打印出0x0,因为将vector传递给函数时,它的值被复制到了参数arr中,修改arrvector没有影响。当函数返回后,没有将存储在arr中的值复制到vector中。图3-8说明了内存分配情况:左图显示arr被赋新值之前的内存状态;中图显示allocateArray函数中的malloc函数执行且初始化数组后的内存状态,arr变量被修改为指向堆中的某个新位置;右图显示函数返回后程序栈的状态。此外,这里有内存泄漏,因为我们无法再访问地址600处的内存块了。

enter image description here

图3-8:传递指针

实现自己的free函数

由于free函数存在一些问题,因而某些程序员创建了自己的free函数。free函数不会检查传入的指针是否是NULL,也不会在返回前把指针置为NULL。释放指针之后将其置为NULL是个好习惯。

有了指针传递数据中的基础知识,我们给出下面这个free函数的实现,可以给指针赋NULL。此处需要我们给它传递一个指针的指针:

void saferFree(void **pp) {
    if (pp != NULL && *pp != NULL) {
        free(*pp);
        *pp = NULL;
    }
}

saferFree函数调用实际释放内存的free函数,前者的参数声明为void指针的指针。使用指针的指针允许我们修改传入的指针,而使用void类型则可以传入所有类型的指针。不过,如果调用这个函数时没有显式地把指针类型转换为void会产生警告,执行显式转换就不会有警告。

下面这个safeFree宏调用saferFree函数,执行类型转换,并使用了取地址操作符,这样就省去了函数使用者做类型转换和传递指针的地址:

#define safeFree(p) saferFree((void**)&(p))

下面的代码片段说明了这个宏的用法:

int main() {
    int *pi;
    pi = (int*) malloc(sizeof(int));
    *pi = 5;
    printf("Before: %p\n",pi);
    safeFree(pi);
    printf("After: %p\n",pi);
    safeFree(pi);
    return (EXIT_SUCCESS);
}

假设malloc返回的内存位于地址1000,那么这段代码的输出是1000和0。第二次调用safeFree宏给它传递NULL值不会导致程序终止,因为saferFree函数检测到这种情况并忽略了这个操作。

赞(2)

评论 抢沙发

评论前必须登录!

 

C指针