试图在C / free问题中从队列中返回一个字符串

时间:2012-01-19 20:29:41

标签: c string malloc

我已经在CSC课程的实验室工作了一段时间,不幸的是我对C有点生疏(你可能会从代码中注意到)。我遇到了两个与内存管理有关的特殊问题。

1)在dequeue操作中,我试图从队列末尾的节点返回一个字符串值。因为我还试图使用free()并在检索数据后终止该节点,所以我需要使用类似strcpy()的方法来获取数据。每当我尝试使用strcpy时,程序会出现段错误,而Valgrind声称无效的r / w。

2)dequeue也没有正确更新stringQueue结构,原因我无法理解。我有类似的堆栈代码,其中的更改仍然存在,但我可以整天继续运行,并且实际上不会删除结束节点。

相关代码:

typedef struct node { 
  char data [strMax];
  struct node * next;
} queueNode;

typedef struct {
  queueNode * head;
  queueNode * tail;
} stringQueue;

char * dequeue(stringQueue *queue) {
  char * data = malloc(strMax * sizeof(char));
  if(empty(*queue)) {
    return "Null list!";
  }
  else if(!(queue->head)->next) { // One item in the queue.
    data = (queue->head)->data;
    //free(queue->head);
    queue->head = NULL;
    queue->tail = NULL;
  }
  else { // Multiple items in the queue.
    data = (queue->tail)->data;
    free(queue->tail);
    queueNode * trace = queue->head;
    while(trace->next) // Seek the last node in the queue.
      trace = trace->next;
    queue->tail = trace;
  }
  return data;
}

3 个答案:

答案 0 :(得分:1)

你的主要问题是像data = (queue->head)->data;这样的行。你不能像这样分配数组。你应该memcpy。 (strcpy用于以空字符结尾的字符串,我猜它不是这样)

编辑:您也可以使用strncpy,以避免缓冲区溢出。

答案 1 :(得分:0)

您可能希望首先将data声明为char * = NULL。然后,当您想要返回时,请使用data = asprintf("%s", (queue->tail)->data);。这只会在需要时进行字符串分配和复制,并且只需要大小。然后,您的主叫代码必须承担责任以释放该数据本身。

您当前在堆内存中的节点结构中有char[]。稍后,您将设置指向结构的data成员的指针,然后在内存中释放结构。你留下了一个“悬垂指针”,指向结构曾经的位置。试图使用该指针将以几乎某些厄运结束(或更糟糕的是,不可预测的行为)。

答案 2 :(得分:0)

我发现您的代码存在一些问题......

首先,您不测试您的queue参数不是NULL。然后你还没有包含empty()的定义,但可能测试queue-> head为NULL应该告诉你列表是空的。在你测试它是一个有效的指针之前,你在这里取消引用它,非常危险。

其次,您正在使用一些未正确使用的数据。当您执行感情data = (queue->head)->next;时,您正在丢失指向已分配内存的指针,您可能希望在此处strncpy()执行strncpy(data, queue->head->data, strMax)。在此之后,您可以取消注释free()。调用你的dequeue函数的函数将在以后不再使用时将free()该字符串。 为什么不在您确定列表不为空时才分配data?如果您不想这样做,那么您必须free()不要使用malloc内存。

请参阅下面的代码。

queueNode* find_before_tail(stringQueue* queue)
{
   queueNode* node = NULL;

   if (!queue || !queue->head)
      return NULL;

   node = queue->head;
   while (node->next != queue->tail && node->next)
      node = node->next;

   return node;
}

char * dequeue(stringQueue *queue) {
  char *data = NULL;
  queueNode* to_queue = NULL;

  if(!queue || !queue->head) {
    /* Nothing to dequeue here... */
    return NULL;
  }

  data = malloc(strMax * sizeof(char));
  if (!data) {
    printf("Error with malloc()...\n");
    return NULL;
  }

  /* Only one element */
  if(!(queue->head)->next == queue->head) {
    strncpy(data, queue->head->data, strMax);

    free(queue->head);

    queue->head = NULL;
    queue->tail = NULL;
  }
  else {
    strncpy(data, queue->tail->data, strMax);

    to_dequeue = queue->tail;

    queue->head = queue->head->next;

    queue->tail = find_before_tail(queue);
    if (!queue->tail)
       return NULL;
    queue->tail->next = NULL;

    free(to_dequeue);
  }

  data[strMax - 1] = 0;

  return data;
}

其余的代码可能还有其他一些问题,从这个问题来判断,但希望它能给你一些基础。

使用您的队列代码进行编辑

这里你再次没有测试malloc()的返回值。这是一个带有非循环链表的版本(我还更新了上面的dequeue()函数来处理这个问题。)

int enqueue(stringQueue *queue, char *item)
{
   queueNode * newNode = NULL;

   if (!queue || !item)
      return EINVAL;

   newNode = malloc(sizeof(queueNode));
   if (!newNode) {
      perror("malloc()");
      return errno;
   }

   strncpy(newNode->data, item, strMax);
   newNode->data[strMax - 1] = 0;

   if (!queue->head) {
       /* Element is queue and tail */
       queue->tail = newNode;
   }

   newNode->next = queue->head;
   queue->head = newNode;

   return 0; /* Everything was fine */
}

我没有测试过代码,但它应该与此非常相似。在这种情况下,当您只有一个元素时,this_element->nextNULL且未指向自身。