【问题标题】:Freeing an array of dynamically allocated pointers释放动态分配的指针数组
【发布时间】:2019-11-30 19:35:00
【问题描述】:

我正在准备考试,我对提供给我的样题有疑问:

使用原型void free_array(void *a[], int length) 实现函数free_array。该函数的目的是释放与传递给函数的第一个参数中的每个数组元素相关联的动态分配的内存。该函数必须处理两个或多个数组条目指向同一内存的情况(只有一个空闲可以应用于单个内存位置)。

我的第一个想法是将每个已释放的索引设置为 NULL,因为在 NULL 上调用 free() 是无害的。所以我做了以下事情:

void free_array(void *a[], int length) { 
   int i;
   for (i = 0; i < length; i++) {
      free(a[i]);
      a[i] = NULL;
   }
}

提供的解决方案与我的完全不同,我不完全理解他们在做什么。这是我得到的答案:

void free_array(void *a[], int length) { 
    int i, j;
    for (i = 0; i < length; i++) {
        if (a[i] != NULL) {
            free(a[i]);
            for (j = i + 1; j < length; j++) {
                if (a[j] == a[i]) {
                    a[j] = NULL;
                }
            }
        }
    }
}

我真的很困惑这里发生了什么。似乎他们正在释放每个条目并将具有相同内容的条目标记为 NULL。但是,我认为我们不应该重新访问释放的内存?另外,我的方法还能用吗?

【问题讨论】:

  • 看起来数组可能有多个条目指向同一个内存。多次释放相同的内存是错误的,因此对于每个条目,您必须检查其他条目是否指向同一块,并将其清除。
  • 你的方法根本行不通。它甚至不会尝试处理指向同一位置的多个指针的情况。提供的解决方案具有未定义的行为,但不是因为它正在访问释放的内存。
  • 我认为如果我将 a[i] 设置为 NULL,那么每个指向与 a[i] 相同地址的指针也将等于 NULL。这是不正确的吗?
  • @redblacktrees 无效有效。对free 的调用应该发生在 内部循环之后。
  • 这是无效的,这也是他们的解决方案具有未定义行为的原因。但是,代码没有访问已释放的内存。只是在free(x)之后x的值变得不确定,所以你甚至不能检查它。

标签: c free dynamic-memory-allocation


【解决方案1】:

代码的意图是数组中的多个位置可以指向同一个元素,但只能释放一次。内部循环会将指向同一内存块的尚未处理的指针设置为指向 null,这样它们就不会被尝试释放两次。

但是,实现有一个严重的问题:C 标准规定 free(x) x 的值变成indeterminate。不确定意味着该值可以是您检查变量的任何实例中的任何值。因此,C 编译器假定指针与任何有效指针的指针都不匹配并优化“几乎正确”是合法的

void free_array(void *a[], int length) {
   int i, j;
   for (i = 0; i < length; i++) {
      if (a[i] != NULL) {
          free(a[i]);
          for (j = i + 1; j < length; j++) {
              if (a[j] == a[i]) {
                  a[j] = NULL;
              }
           }
     }
}

void free_array(void *a[], int length) {
    int i, j;
    for (i = 0; i < length; i++) {
        if (a[i] != NULL) {
            free(a[i]);
            for (j = i + 1; j < length; j++) {
                if (false) {
                }
            }
        }
    }
}

void free_array(void *a[], int length) {
    int i, j;
    for (i = 0; i < length; i++) {
        if (a[i] != NULL) {
            free(a[i]);
        }
    }
}

正确的解决方案必须调用free之前进行指针比较:

void free_array(void *a[], int length) {
    int i, j;
    for (i = 0; i < length; i++) {
        if (a[i] != NULL) {
            for (j = i + 1; j < length; j++) {
                if (a[j] == a[i]) {
                    a[j] = NULL;
                }
            }
            free(a[i]);
        }
    }
}

【讨论】:

【解决方案2】:

例如

假设你有一个字符串数组

char* ar[5]; 

你分配了一些字符串,让 0 和 4 索引指向同一个分配的字符串

ar[0] = ar[4] = strdup("mycommonstring");
ar[1] = strdup("hello");
ar[2] = strdup("world");
ar[3] = strdup("!");

有了你的回答,你将首先释放 ar[0] 指向的内容,不幸的是 ar[4] 仍然指向相同的内存位置,但是当调用 free(ar[0]) 时,它指向的内存位置无效。稍后在ar[4]上调用free时会报错。

给定的示例确保指向同一位置的所有指针都将设置为 NULL,以避免将无效地址传递给空闲函数。所以不,内存位置不再被访问,只有 指针 指向内存位置

【讨论】:

    猜你喜欢
    • 2014-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    • 1970-01-01
    相关资源
    最近更新 更多