队列结构在被调用一次后丢失元素值

时间:2013-11-02 09:19:45

标签: c string pointers queue

我在C中编写一个队列结构,它接受字符串(char指针)作为元素。我遇到的问题是,在打印元素值一次后,它会在之后显示为null。

使用下面的代码,我期待这个输出:

1 Hello
1 Hello
2 World
Hello World
1

但我得到了这个输出:

1 Hello
1 (null)
2 World
(null) (null)
1

有人可以告诉我我做错了什么吗?

#include <stdlib.h>
#include <stdio.h>

struct Node {
    struct Node *next;
    char* element; 
};
struct Queue {
    int size;
    struct Node *head;
    struct Node *tail;

};

void Enqueue(struct Queue *q, char* str) {
    struct Node newNode = {0,str};
    if(q->size == 0) {
        q->head = &newNode;
    }
    else {
        q->tail->next = &newNode;   
    }
    q->tail = &newNode;
    q->size = q->size + 1;
}
char* Dequeue(struct Queue *q) {
    if(q->size < 0) {   
        return -1;
    }
    char* tbr = q->head->element;
    struct Node* oldNode = q->head;
    q->head = oldNode->next;
    q->size = q->size - 1;
    if(q->size == 0) {
        q->tail = NULL;
    }
    return tbr;
}
int IsEmpty(struct Queue *q) {
    return q->size == 0;
}
char* Peek(struct Queue *q) {
    return q->head->element;
}
int main() {
    struct Queue q = {0};
    Enqueue(&q,"Hello");
    printf("%d %s\n",q.size,q.head->element);
    printf("%d %s\n",q.size,q.head->element);
    Enqueue(&q,"World");
    printf("%d %s\n",q.size,q.head->next->element);
    printf("%s %s\n",Dequeue(&q),Dequeue(&q));
    printf("%d\n",IsEmpty(&q));
    printf("%s %s\n","Hello","World");
    Dequeue(&q);
    return 0;
}

2 个答案:

答案 0 :(得分:1)

Enqueue()插入对堆栈内存的引用,该内存仅在本地有效:

void Enqueue(struct Queue *q, char* str) {
  struct Node newNode = {0,str}; 

一旦函数返回,该存储就被释放,并且访问会激活undefine行为。

要解决此问题,请从堆中动态分配内存:

void Enqueue(struct Queue *q, char* str) {
  struct Node * pnewNode = calloc(1, sizeof(*pnewNode);
  if (NULL == pnewNode)
  {
    perror("calloc() failed");
    exit(EXIT_FAILURE);
  } 

  pnewNode->element = str;

  if(q->size == 0) {
    q->head = pnewNode;
  }
  else {
    q->tail->next = pnewNode;   
  }
  q->tail = pnewNode;
  q->size = q->size + 1;
}

请注意,动态分配的内存需要free() ed。

答案 1 :(得分:1)

Enqueue内,您不能使用局部变量在队列中插入新节点,因为您将在函数的生命周期之外使用它,并且在函数之后销毁局部变量回报。

因此,当您从Enqueue返回时,插入的元素(newNode)指向一个无效的内存位置,当您调用另一个函数时,该位置很可能会被覆盖。所有赌注都已关闭。如果希望节点寿命更长,则必须使用动态分配:

void Enqueue(struct Queue *q, char* str) {
    struct Node *newNode = malloc(sizeof(struct Node));
    newNode->next = NULL;
    newNode->element = str;
    if(q->size == 0) {
        q->head = newNode;
    }
    else {
        q->tail->next = newNode;   
    }
    q->tail = newNode;
    q->size = q->size + 1;
}

另外,请注意,现在您需要free一个节点出列。 Dequeue成为:

char* Dequeue(struct Queue *q) {
    if(q->size < 0) {   
        return -1;
    }
    char* tbr = q->head->element;
    struct Node* oldNode = q->head;
    q->head = oldNode->next;
    q->size = q->size - 1;
    if(q->size == 0) {
        q->tail = NULL;
    }
    free(oldNode);
    return tbr;
}

main内,您的最终Dequeue会导致分段错误,因为队列为空,并且您不会在Dequeue中检查此情况。您可以在Dequeue返回true时不致电IsEmpty,或者在Dequeue中添加代码以检查此案例。

最后注意事项:在main中,此行:

printf("%s %s\n",Dequeue(&q),Dequeue(&q));

不一定会打印Hello World,因为参数评估顺序是实现定义的。例如,在我的机器中,它会打印World Hello