C语言 - 释放数组中的指针

4
我一直试图让我使用malloc初始化的所有指针都在一个指针数组中(或者是指向指针的指针),这样我就可以通过遍历该数组并使用类似free(ptr_array[i])的方法释放每个指针,而不必逐个释放。
我相信这是可能的,但是我的代码有些问题,因为用于释放所有内容的函数(如果可能的话,我宁愿将其保留在单独的函数中)显然无法正确地编辑“子指针”,并且它们似乎被复制,而不是像“主指针”那样。
以下是令我困惑的代码:
#include <stdlib.h>
#include <stdio.h>

void free_test(int** arg){
    printf("\tReceived arg addr: %p\n",arg);
    printf("\tFreeing arg[0] = %p\n",arg[0]);
    //And, just to MAKE SURE that arg[0] is ver, we do this:
    printf("\tIn-function ver[0] = %d\n",arg[0][0]);
    arg[0][0] = 2;
    printf("\tIn-function ver[0] = %d\n",arg[0][0]);

    free(arg[0]); //This one doesn't work...
    free(arg); //This one works though.
}

int main(){
    int** var;
    int* ver;
    int i;
    printf("ver addr 1: %p\n", ver);
    ver = malloc( sizeof(int) * 3 );
    printf("ver addr 2: %p\n", ver);
    for(i=0;i<3;i++){
        ver[i] = 5*i;
    }
    for(i=0;i<3;i++){
        printf("ver[%d]: %d\t(@ %p)\n",i,ver[i],&(ver[i]));
    }
    printf("var addr 1: %p\n", var);
    var = malloc( sizeof(int *) );
    printf("var addr 2: %p\n", var);
    var[0] = ver;
    printf("var[0] addr = %p\n", var[0]);
    free_test(var);
    printf("ver[0] new value: %d\n", ver[0]);
    //free(var); (free(arg) works in the function, so this one throws an error.)
    printf("So far so good, now break.\n");
    free(ver);
    printf("Didn't break. :(\n");
    return 0;
}

目前它给出的输出如下:

ver addr 1: 0x8048679
ver addr 2: 0x8b0f008
ver[0]: 0   (@ 0x8b0f008)
ver[1]: 5   (@ 0x8b0f00c)
ver[2]: 10  (@ 0x8b0f010)
var addr 1: 0xad7ff4
var addr 2: 0x8b0f018
var[0] addr = 0x8b0f008
    Received arg addr: 0x8b0f018
    Freeing arg[0] = 0x8b0f008
    In-function ver[0] = 0
    In-function ver[0] = 2
ver[0] new value: 0
So far so good, now break.
Didn't break. :(

我对C语言不是很擅长,因此希望能得到帮助。特别是由于函数内部改变了ver[0]的值,但外部没有改变,我猜这可能与为什么free(...)无法正常工作有关。

提前感谢。


刚刚测试了你的代码,发现在 llvm-gcc-4.2 上的 free(ver) 处一定会出错。 - Jack
嗯,也许是我的编译器有问题?根据“gcc -v”命令,我正在使用“gcc版本4.6.3(Ubuntu / Linaro 4.6.3-1ubuntu5)”。但是,我无法更改到其他编译器,因为我想要移植代码的机器(据我所知)只有gcc。不过还好,至少我的代码在另一个编译器上按预期工作了。 :) - Devvie
在找到答案后,我观察到的一些额外信息是函数内部的 free(...) 会将指针的第一个位置设置为0,而主函数中的 free(ver) 则将其设置为可能是“随机”的值。这可能是特定于操作系统的实现(或gcc的),但我认为值得一提。 - Devvie
4个回答

1

我的C语言知识已经有些模糊,但如果我没记错,连续的内存释放会导致未定义的行为。因此,你不能保证会收到任何错误。如果我理解正确,这种行为将严重依赖于特定的操作系统、编译器和标准库实现(甚至是特定的程序运行)。

补充:我的VS2012 VC++编译器在free(ver);处明显出现了错误。输出如下:

ver addr 1: CCCCCCCC
ver addr 2: 00B8AFF0
ver[0]: 0       (@ 00B8AFF0)
ver[1]: 5       (@ 00B8AFF4)
ver[2]: 10      (@ 00B8AFF8)
var addr 1: CCCCCCCC
var addr 2: 00B85BF0
var[0] addr = 00B8AFF0
        Received arg addr: 00B85BF0
        Freeing arg[0] = 00B8AFF0
        In-function ver[0] = 0
        In-function ver[0] = 2
ver[0] new value: -17891602
So far so good, now break.

我相信是这样的,但如果我在使用“free(ver)”之后立即重复使用它,那么据我所知它会出现错误。 - Devvie
你是对的,按照lordofire建议使用valgrind测试后,我发现实际上只是操作系统或某些特定情况下没有报错,但确实已经被释放了。谢谢! :) - Devvie

1
我已经在gcc 4.6.3上测试了您的代码,并发现使用valgrind时此程序没有任何内存泄漏。当我在主函数中删除“free(ver)”这一行时,程序正常运行,也没有任何内存泄漏。我认为free_test例程已成功释放了主函数中两个malloc。至于为什么在程序中再次释放变量时没有出现任何coredump取决于不同的操作系统实现。
如果您想检查涉及malloc和free的内存泄漏,请使用valgrind进行测试。

谢谢!最终我还是接受了Petr的答案,因为它确实是一个特定于操作系统的问题,并且回答得很快,但valgrind绝对是一个非常有用的工具,感谢分享! - Devvie
没问题,C语言中的内存处理是非常有趣但也很令人困惑的东西。我相信如果你能看一下或者尝试一下Linux中的malloc和free实现,对你的理解会非常有帮助。 - lordofire

0

使用

"*((arg+0)+0)"

代替[][]。 类似于[][]...[]的语法只会解除指针一次引用。每个[]只改变指针的语义(+1时跳过多少位置),与解除引用的次数无关。


void free_test(int** arg){
printf("\tReceived arg addr: %p\n",arg);
    printf("\tFreeing arg[0] = %p\n",arg[0]);
    //And, just to MAKE SURE that arg[0] is ver, we do this:
    printf("\tIn-function ver[0] = %d\n",arg[0][0]);
    arg[0][0] = 2;
//  ^^^^^^^^^^^^^^ here, arg[0][0] is the same as arg[0], so arg[0] is changed, not the location arg[0] points to.
    printf("\tIn-function ver[0] = %d\n",arg[0][0]);

    free(arg[0]); //This one doesn't work...
    free(arg); //This one works though.
}

不过,问题在于它还指向了arg的起始位置,因此最终只是尝试再次释放arg。 编辑:哎呀,抱歉,我漏掉了星号。谢谢。 :) - Devvie
然而,由于某种原因,在函数内部尝试执行free(*(*(arg+0)+0));时它会崩溃。不过,这至少是一种有趣的方法,再次感谢。:) 编辑:哦,我刚看到你最新的编辑。是的,那将会有所不同-不过,它确实将ver[0]更改为2,这正是我在那里想要的。因此,arg[0][0] = 2;可以按预期工作。 - Devvie
@Devvie 当您使用指针所指向的位置时,请使用 *;当您释放指针时,请勿使用 *。 - SliceSort
我知道这不完全是同一件事,但如果arg[0]是ver,那么arg[0][0]应该是ver[0]。实际上,如果我在我的初始代码中注释掉free(arg[0]);,那么ver[0]将显示为2(而且只有当arg[0][0] = 2;发生时才会设置为2)。 - Devvie
arg[0][0] 实际上是 arg[0],并且是 ver(不带 [0])。 - SliceSort

0

为什么你的代码里有这个注释?

free(arg[0]); //This one doesn't work...

我认为它确实起作用了,并且它释放了数据变量所指向的内存,然后你又释放了它。
你的调试数据显示

ver addr 2: 00B8AFF0
Freeing arg[0] = 00B8AFF0

如果你注释掉第一次释放它的代码,仍然失败吗?
并且你在第一次释放后继续使用它:

printf("ver[0] new value: %d\n", ver[0]); /// but you already freed that

尝试使用

void *xmalloc(int s) {
  char *ret = malloc(s);
  printf("malloc(%d)==%p\n", s, ret);
  return ret;
}
void xfree(void *b) {
  printf("free(%p)\n", b);
  free(b)
  return;
}
#define malloc xmalloc
#define free xfree

我已经很久没有用C语言工作了。
初始化你的变量。

int** var = NULL;
int* ver = NULL;

在释放数据块之前,请将其设置为全零(或其他某个值)。
memset(void_ptr, 0, sizeof_your_data);

如果删除所有那些print语句,阅读你的代码会容易得多...尽管我知道它们对于调试很有用...试一下吧(先备份)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接