【问题标题】:Trying to return a string from a queue in C/free problems尝试从 C/free 问题中的队列返回字符串
【发布时间】:2012-01-19 20:29:41
【问题描述】:

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

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

2) 由于我无法理解的原因,出队也没有正确更新 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;
}

【问题讨论】:

  • 你能贴出创建queueNode的代码吗?
  • 已从您的评论中使用您的排队功能更新了我的答案。

标签: c string malloc


【解决方案1】:

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

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

【讨论】:

  • 请记住,如果字符串长于“n”,strncpy 可能不会复制尾随的 0 字节,因此您可能需要自己添加。
  • 谢谢!传入的字符串(在这种特殊情况下)是“Utilities”,作为来自测试用例的直接参数传入。据我记得它仍然是空终止的,但我会给 memcpy 一个机会。
【解决方案2】:

您可能希望首先将data 声明为char * = NULL。然后,当您想退回它时,请使用data = asprintf("%s", (queue->tail)->data);。那只会在需要时进行字符串分配和复制,并且只进行所需的大小。那么您的调用代码必须自行负责释放该数据。

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

【讨论】:

  • 谢谢:就更改 char[] 而言,该段是我们获得的 shell 的一部分,我不相信我能碰它。有没有使用当前格式的解决方法?关于悬空指针,当 free() 未注释时,我一直在尝试使用 strcpy(data, (queue->tail)->data 但我立即收到段错误。关于可能导致这种情况的任何想法?
  • 它是一个空终止的字符串吗?如您所知,字符串的最大长度,我会说总是使用 strncpy。段错误可能是因为缺少\0,它离开了字符串的末尾。
【解决方案3】:

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

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

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

请看下面的代码。

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 并且不指向自身。

【讨论】:

  • 谢谢——这很有帮助。但是你能解释一下 EINVAL 吗?自从我看过 C 以来已经有一段时间了,但我不记得看过 EINVAL。
  • EINVAL 是一个预定义的 C 宏,用于以标准方式返回错误代码。我建议你看看linux.die.net/man/3/errno,它描述了标准错误代码。请理解,只要您记录并在调用者函数中做正确的事情,您就可以返回任何值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 2020-05-26
  • 2021-11-20
  • 2015-01-13
  • 1970-01-01
相关资源
最近更新 更多