簡體   English   中英

在C中從多維數組釋放內存的問題,free()導致程序崩潰

[英]Problem freeing memory from multidimensional array in C, free() crashes program

我正在創建一個雙端隊列以將C存儲在C中,當我調用free()函數時,程序崩潰。 我已經實現了類似的結構,但只存儲整數,並且沒有遇到任何問題,但這似乎使我有些麻煩。 我創建了一個包含多維數組或字符的結構,我想也許我沒有正確使用指針? 我已經搜索了很多,無法解決它。主要的關注領域是當我從ain主體調用clear()時。 依次調用free(),程序將停止。 :-(任何幫助都將非常有用。

#include <stdio.h>

#define MAX 20 // number of characters for word

typedef struct {
  char **deque;
  int   size;
  int pFront;
  int pRear;
} deque;

typedef int bool;
enum { false, true };

void initDeque(deque *d, int initialSize) 
{
  d->size = initialSize;
  d->pFront = -1;
  d->pRear = -1;
  d->deque = (char **)malloc(sizeof(char*)*initialSize);
  int idx;
  for(int idx = 0; idx < d->size; idx++)
  {
    d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char));
    d->deque[idx] = "";
  } 
  printf("d->size: %zu\n", d->size);
}

void clear(deque *d) {
  if(d->pFront == -1)
  {
    printf("Queue is empty\n");
  }
  else
  {
    printf("Attempting to clear...\n");
    for(int idx = 0; idx < d->size; idx++)
    {
      printf("Attempting to clear columns...");
      free(d->deque[idx]);
    }
    printf("Attempting to clear rows...");
    free(d->deque);
    printf("Freed!!!!\n");
    d->deque = NULL;
    d->size = 0;
    d->pFront = -1;
    d->pRear = -1;
  } 
}


bool isEmpty(deque *d)
{
  if(d->pFront == -1){
    return true;
  }
  else
  {
    return false;
  }
}
bool isFull(deque *d)
{
  if(d->size == d->pRear+1)
  {
    return true;
  }
  else
  {
    return false;
  }
}

void display(deque *d)
{
  if(isEmpty(d)){
    printf("empty\n");
  }
  else{
    printf("Deque Values:\n");
    int idx;
    for(int idx = 0; idx <= d->pRear; idx++)
    {
      printf("Index: %zu\tValue: %s\n", idx, d->deque[idx]);
    } 
    printf("Size: %zu\n", d->size);
  } 
}
void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{ 
  if(isFull(d))
  {
    printf("Is Full\n");
    int idx;
    deque dTemp;
    initDeque(&dTemp, d->size);
    printf("dTemp Initialised\n");
    for(idx = 0; idx < d->size; idx++)
    {
      dTemp.deque[idx] = d->deque[idx];
    }
    printf("deque copied to dTemp:\n");
    for(idx = 0; idx < d->size; idx++)
    {
      printf("dTemp[%zu]: %s\n", idx, dTemp.deque[idx]);
    }
    clear(&d);
    printf("d cleared\n");
    initDeque(&d, dTemp.size*2);
    printf("New deque of double length initialised\n");
    for(idx = 0; idx < dTemp.size; idx++)
    {
      d->deque[idx] = d->deque[idx];
    }
    printf("dTemp Copied to new deque\n");
    clear(&dTemp);
    printf("dTemp Cleared\n");
    char **tmp = realloc( d->deque, sizeof (d->deque) * (d->size*2) );
    if (tmp)
    {
        d->deque = tmp;
        for (int i = 0; i < d->size; i++)
        {   
            d->deque[d->size + i] = malloc( sizeof(char) * MAX );
        }
    }
  }
  printf("Appending to rear.. %s\n", item);
  d->pRear++;
  d->deque[d->pRear] = item;
  if(d->pFront == -1)
      d->pFront = 0;
}
int main(void)
{
    deque d;
    initDeque(&d, 5);
    rAppend(&d, "when");
    rAppend(&d, "will");
    rAppend(&d, "wendy");
    rAppend(&d, "walk");
    rAppend(&d, "with");
    display(&d);
    clear(&d);
  return 0;
}

問題是您正在靜態鏈“ when”,“ will”,...上調用free()。

您可以在函數void rAppend(deque *d, char item[])替換插入:

  d->deque[d->pRear] = item;

有:

  d->deque[d->pRear] = strdup(item);

這樣做的話,鏈是在堆中分配的,並且沒有堆。
之后,代碼中還有其他問題,但是它運行時沒有崩潰。

主要問題似乎是您不理解復制/分配指針與復制/分配指針指向 的數據之間的區別。 其次,似乎您可能不喜歡不指向任何東西的指針的實用程序,尤其是空指針。 一些細節如下。


您正在為一堆字符串動態分配空間...

  for(int idx = 0; idx < d->size; idx++) { d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char)); 

...然后通過將指向每個指針的指針替換為指向空字符串文字的指針來泄漏所有空間:

  d->deque[idx] = ""; } 

好像泄漏還不夠嚴重,不允許您釋放字符串文字或修改其內容,但是無論何時您clear()它,您都嘗試對保留在出隊中的任何指針進行此操作。 這可能是某些錯誤的原因。

如果要將每個分配的字符串設置為空,則修改其內容,而不是替換指向它的指針。 例如:

    d->deque[idx][0] = '\0';

但是實際上,您可能甚至不需要這樣做。 您已經在執行簿記,以了解哪些數組包含有效(字符串)數據,哪些不包含有效數據,而這足以完成正確的操作。 假設您首先維護字符串的副本。

但這還不是全部。 當您將rAppend()元素添加到雙端隊列時,您rAppend()遇到類似的問題。 您創建一個臨時雙端隊列,然后將字符串指針從原始雙端隊列復制到該臨時域:

 dTemp.deque[idx] = d->deque[idx]; 

這不僅會泄漏臨時雙端隊列中的原始(空)數據,還會使該雙端隊列的內容與主雙端隊列的別名相同。 因此,當您以后清除臨時雙端隊列時,可以釋放原始字符串中的所有字符串指針。 隨后使用或釋放​​它們會產生不確定的行為。

也許您反而希望將主雙端隊列的所有元素strcpy()放入temp並返回,但我建議改為跳過temp雙端隊列,並遵循以下幾條原則:

void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{ 
  if(isFull(d))
  {
    printf("Is Full\n");
    char **tmp = realloc(d.deque, d->size * 2);

    if (tmp)
    {
        d->deque = tmp;
        for (int i = 0; i < d->size; i++)
        {   
            // Copied from the original, but see below
            d->deque[d->size + i] = malloc( sizeof(char) * MAX );
        }
        d->size * 2;
    }  // else?
  }
  printf("Appending to rear.. %s\n", item);
  d->pRear++;
  // Oops, this is another leak / aliasing issue:
  d->deque[d->pRear] = item;
  if(d->pFront == -1)
      d->pFront = 0;
}

臨時雙端隊列的全部內容都讓我迷失了,因為您需要做的realloc()始終保留原始數據(只要成功就可以)。

但是,也請注意,這仍然存在別名問題:您已經為帶有雙引號的雙端隊列元素添加了別名,並泄漏了為該元素分配的內存。 此外,當清除雙端隊列時,您將為持有指針的每個人釋放該字符串。 或者至少您嘗試這樣做。 不允許這樣做來字符串文字。

我建議根本不在雙端隊列中為單個字符串分配空間,也不要釋放它。 繼續使用賦值將元素存儲在雙端隊列中,理解並接受這些是別名。 這將更類似於您對int的實現。

#include<memory>
#include<iostream>
using namespace std;
struct S {
    S() { cout << "make an S\n"; }
    ~S() { cout << "destroy an S\n"; }
    S(const S&) { cout << "copy initialize an S\n"; }
    S& operator=(const S&) { cout << "copy assign an S\n"; }
};
S* f()
{
    return new S;   // who is responsible for deleting this S?
};
unique_ptr<S> g()
{
    return make_unique<S>();    // explicitly transfer responsibility for deleting this S
}
int main()
{
    cout << "start main\n";
    S* p = f();
    cout << "after f() before g()\n";
//  S* q = g(); // this error would be caught by the compiler
    unique_ptr<S> q = g();
    cout << "exit main\n";
    // leaks *p
    // implicitly deletes *q
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM