【问题标题】:Queue struct losing element values after being called once队列结构在被调用一次后丢失元素值
【发布时间】:2013-11-02 09:19:45
【问题描述】:

我正在用 C 语言编写一个队列结构,它将字符串(字符指针)作为元素。我遇到的问题是,在打印一次元素值之后,它就变成了 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;
}

【问题讨论】:

    标签: c string pointers queue


    【解决方案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 中检查这种情况。要么当 IsEmpty 返回 true 时不调用 Dequeue,要么在 Dequeue 中添加代码来检查这种情况。

    最后注:main,这一行:

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

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

    【讨论】:

    • 这解决了我的问题!谢谢!
    【解决方案2】:

    Enqueue()在栈上插入对内存的引用,只在本地有效:

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

    一旦函数返回,这个存储空间就会被释放,并且访问会引发未定义的行为。

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

    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。

    【讨论】:

    • 我该如何解决这个问题?使用 malloc?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-26
    • 1970-01-01
    • 2017-02-13
    相关资源
    最近更新 更多