【问题标题】:C Memory assignment, Segmentation Fault / Double Free In Array of StructsC 内存分配、分段错误/结构数组中的双释放
【发布时间】:2018-01-29 12:35:27
【问题描述】:

据我了解,分段错误是您尚未正确分配内存时,而 Double free 是您尝试释放已释放的内存时?

增加结构数组大小的正确方法是什么,以及您实际需要在哪里/哪些部分释放?

我有一个结构:

struct Data {
    // Some variables
} 

我正在初始化这些结构的数组:

int curEntries = 100;
int counter = 0; 
struct Data *entries = (struct Data *)malloc(curEntries * sizeof(struct Data));

当我从 bin 文件中读取数据到这个数组中并填充每个结构时,程序会一直运行,直到需要超过 100 个结构。当时,我有以下代码来重新分配数组:

if (counter == curEntries - 1) { // counter = current index, curEntries = size of the array
    entries = (struct Data *)realloc(entries, curEntries * 2 * sizeof(struct Data));
    // struct Data *temp = (struct Data *)realloc(entries, curEntries * 2 * sizeof(struct Data));
    // free(entries);
    // entries = temp;
    // free(temp);
}

我现在使用的行 (entries = . . .) 有效,但显然是错误的,因为我没有释放任何东西,对吧?

但是当我尝试使用注释掉的代码时,我得到了一个双重免费错误

最后,(因为有一系列自动测试),显然我需要在我的代码的其他部分使用 malloc 等。我还应该/我需要在哪里分配内存?

【问题讨论】:

标签: c arrays free dynamic-memory-allocation realloc


【解决方案1】:

我现在使用的行 (entries = . . .) 有效,但显然是错误的,因为我没有释放任何东西,对吧?

这是错误的只有在 realloc() 失败时。成功后,realloc()如有必要会自动释放先前分配的块(如果它是同一个块并且系统可以简单地更改大小,则可能没有必要)。

所以,常用的成语是这样的:

mytype *var = malloc(...);

// ...

mytype *tmp = realloc(var, ...);
if (!tmp)
{
    free(var);
    return -1; // or whatever error
}
var = tmp;

// ...

free(var);

【讨论】:

  • 那么 if 条件检查 realloc 是否失败?能否请您解释一下为什么那时您会 free(var),您不会因为没有成功的 realloc 而想保留它吗?
  • 可以保留它,只是大多数时候,原始大小的块对你的程序没有帮助,所以你free()它并指出错误.
【解决方案2】:

首先,请不要使用之类的格式

 pointerVar = realloc (pointerVar , newsize);  // use the same pointer variable

因为,如果 realloc() 失败,您也会擦除实际指针。

对于realloc() 失败的情况,来自C11,第 7.22.3.5 章,

realloc 函数返回 ... 如果新对象不能被调用,则返回一个空指针 已分配。

[....] 如果新对象的内存不能 已分配,旧对象不会被释放,其值不变。

使用 realloc 的正确方法是

  tempPtr = realloc (oldPtr, newSize);

  if ( tempPtr )  //allocation successful, oldPtr is `free()`-d can be reused now
  {
      oldPtr = tempPtr;
  } // and continue using `oldPtr`
  else
  {
      // some error handling
      // can still make use of `oldPtr`
   }

也就是说,realloc() 会自行清理之前的内存分配,以防新内存分配成功,您无需释放它。

引用C11,同一章节

void *realloc(void *ptr, size_t size);

realloc 函数释放ptr 指向的旧对象 并返回一个 指向具有size 指定大小的新对象的指针。

所以,如果你的代码被注释掉了

struct Data *temp = (struct Data *) realloc(entries, curEntries * 2 * sizeof(struct Data));
  //assume success, memory pointed to by entries will be automatically freed

free(entries);
   // now, you're trying to free already freed memory, UB....

entries = temp;
free(temp);

【讨论】:

  • 你说如果 realloc 失败,我会擦除指针,但是在 C11 中我不需要担心,因为“旧对象没有被释放”,对吗?
【解决方案3】:

您收到双重释放错误,因为您对realloc() 的调用成功,因此前一个指针已被释放,而您却调用了free(entries)。库有时可以确定一个块已被释放,但这种健全性检查并不总是有效的。 C 标准对此不提供任何保证,将释放的指针传递给 free() 具有未定义的行为。

在具有内存保护的系统上,当您尝试读取或写入尚未分配给您的进程或已对进程无效的内存地址时,可能会发生分段错误。在库可以确定该块已被释放之前,取消引用指向已释放块的指针可能会导致分段错误。

重新分配数组的方案应该是这样的:

size_t curEntries = 100; // size of the array
size_t counter = 0;      // current index

...

if (counter == curEntries) {
    // array is full, try and reallocate to a larger size
    size_t newSize = curEntries * 2;
    struct Data *newArray = realloc(entries, newSize * sizeof(*newArray));
    if (newArray == NULL) {
        // cannot reallocate, out of memory.
        // handle this error, entries is still valid.
        abort();
    } else {
        // array was reallocated possibly to a different address
        // entries is no longer a valid pointer
        entries = newArray;     // update array pointer
        curEntries = newSize;   // update number of entries
    }
}

【讨论】:

  • 先生,为什么是 malloc sizeof(*newArray) 而不是 sizeof(newArray) ?
  • @snr: T *p = malloc(count * sizeof(*p)); 是分配count 类型为T 的元素数组的更安全方法。使用sizeof(*p)p指向的对象的大小是正确的,不依赖于p的类型。它避免了p 的定义与分配大小中使用的类型之间的潜在不一致。 sizeof(newArray) 将是指针的大小,而不是它指向的结构的大小。
  • 谢谢我会那样做
猜你喜欢
  • 1970-01-01
  • 2016-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多