【问题标题】:Why do I override my struct? Implementing queues but it doesn'work为什么我要覆盖我的结构?实现队列但它不起作用
【发布时间】:2017-05-19 17:09:57
【问题描述】:

我需要在 C 中编写队列结构以进行分配。节点有一个指向下一个节点的指针和值(到目前为止,很正常)。但是,由于我需要将它与线程一起使用,我将 malloc 堆上的所有容量。 但是,节点和队列是这样定义的:

//Element of a queue
struct queue_node {
    // Pointer to next element in the queue
    struct queue_node* next;
    // Value/Data of the queue element
    int value;
};

// Queue data structure
    struct queue {
    // Head of the linked list
    struct queue_node* head;
    // Max capacity of the queue
    int capacity;
    // Current size of the queue. size <= capacity, always
    int size;
};

我遇到的问题是推送元素,因为我没有任何关于分配内存的开始或结束的信息。所以我决定让头节点始终是空间中的第一个节点,这样我就可以使用容量 我编写了这样的基本功能:

struct queue* queue_new(int capacity){

    struct queue_node* head1 = malloc(sizeof(struct queue_node)*capacity);

    struct queue* ret = malloc(sizeof(struct queue));

    /*
    struct queue_node head2;
    head2.next = NULL;

    (*head1) = head2;
    */
    (*head1).next = NULL;

    (*ret).head = head1;
    (*ret).size = 0;
    (*ret).capacity = capacity;

    return ret;

    }



void queue_delete(struct queue* queue){
    free((*queue).head);
    free(queue);
    }

所以。但是,当我想将某些东西推入队列时,我会遇到麻烦。显然,要做的第一件事,就是填满脑袋。这似乎奏效了。但是将元素附加到头节点不会:

int queue_push_back(struct queue* queue, int value){

    if((*queue).size >= (*queue).capacity){
        return -1;
    }else if((*queue).size == 0){
        (*(*queue).head).value = value;
        (*queue).size++;
        return (*queue).size;
    } else{



        if((*(*queue).head).next == NULL){          ////((*queue).head + sizeof(struct queue_node))

            printf("Intern queue size 1: %d\n", (*queue).size);

            (*(*queue).head).next = ((*queue).head + sizeof(struct queue_node));
            printf("Error here?\n");
            (*(*(*queue).head).next).value = value;
            printf("Error here 2?\n");
            (*(*(*queue).head).next).next = NULL;
            printf("Error here 3?\n");


            printf("Intern queue size 2: %d\n", (*queue).size);
            printf("Intern queue capacity: %d\n", (*queue).capacity);

            (*queue).size++;
            return (*queue).size;

        }
}

我跳过了常见情况的代码,因为这甚至不起作用。出于某种原因,如果我想推送第二个元素,它会覆盖我的队列结构。我不知道为什么。 有人可以帮我告诉我,我哪里做错了吗?

【问题讨论】:

  • 无论如何,请使用-&gt; 运算符...(*(*(*queue).head).next).next 不清晰,应该是queue-&gt;head-&gt;next-&gt;next 然后请编辑您的代码以创建Minimal, Complete, and Verifiable example。这绝对是错误的,因为它不分配内存:(*(*queue).head).next = ((*queue).head + sizeof(struct queue_node));
  • 如果一个答案解决了你的问题,不要只是在标题中“解决”了,而是mark the answer as accepted

标签: c pointers struct queue malloc


【解决方案1】:

您似乎试图混合两种不同的方法来解决这个问题:

  1. 将队列维护为数组,并且

  2. 将队列维护为链表。

为一个块中的全部节点分配空间,将队列头保持在块的开头,并且首先确实具有固定的队列容量,这些都是类数组使用的特征。另一方面,具有“next”指针的元素节点结构是链表的形式。

如果您将队列作为一个数组进行管理,那么 next 指针是多余的,而且如果您实际使用它们,它们确实会受到限制。相反,您始终可以通过指向节点块开头的指针和节点索引来识别和导航到节点:my_queue_ptr-&gt;head[node_num]。您还可以根据队列的当前大小确定下一个可用节点:my_queue_ptr-&gt;head[my_queue_ptr-&gt;size]

但是,每当您使一个节点出队时,您都必须将所有其他节点——或者至少是它们的数据——向前移动一个位置。如果你移动整个节点,那么你就搞砸了它们的next 指针,因为每个指向位置的东西与以前不同,具有不同的意义。

另一方面,如果您将队列作为链表进行管理,那么将所有节点分配在一个块中是没有意义的。相反,为您入队的每个值分配一个新节点,并取消分配您出队的每个值的节点,这将是惯例。在这种情况下,您将在第一个元素入队和任何元素出队时修改队列的head 指针。如果您不维护指向当前尾部的指针(目前您没有),那么每次将元素排入队列时,您都需要遍历列表以找到尾部节点,并将新节点附加到那里。


更新:

如果您仍然按照您所描述的进行,对我来说唯一有意义的前进方式是采用基于数组的方法,并完全忽略数据结构的链表方面。您提出的queue_new()queue_delete() 函数对此是合理的。另一方面,您的 queue_push_back() 甚至内部不一致,更不适合类似数组的方法。

在此之前,我会详细介绍,但是,我想指出您的代码不必要地难以阅读。至此,您肯定已经了解了-&gt; 运算符;它专门设计用于简化指向结构的指针的使用,尤其是易于使用指向结构的指针链。这是queue_push_back() 函数的第一部分,重写为使用-&gt;;呈现的部分与您原件的相应部分完全相同:

int queue_push_back(struct queue* queue, int value){

    if (queue->size >= queue->capacity) {
        return -1;
    } else if (queue->size == 0) {
        queue->head->value = value;
        queue->size++;
        return queue->size;
    } else {

    // ...

}

很多更容易阅读,至少对我来说。现在,分心更少,更容易看到您设置的头节点的唯一属性是它的value。您没有设置它的next 指针。如果您理解了我的建议,那么您就会认识到这实际上很好——您将使用数组中的索引来访问元素,而不是链接,这充其量是多余的。

但是现在考虑一下当你推送下一个元素时你会尝试做什么。首先是测试头节点的next 指针的值,您从未设置过该指针。未定义的行为结果。现在您可以管理链接并使用它们(尽管如果您想支持容量大于 2 的队列,您需要比现在更复杂的东西),但正如我所说,我的建议是完全忽略链接。

已经验证队列有空间容纳另一个元素,您可以直接以queue-&gt;head[queue-&gt;size] 访问该元素:

    queue->head[queue->size].value = value;
    queue->size++;

Lo,这就是它的全部内容。但是等等,它会变得更好!如果你仔细看,你会发现第一个节点的情况和其他节点的情况几乎没有区别。实际上,区别纯粹是句法上的;第一个节点(当queue-&gt;size == 0 时)同样可以由我刚刚提供的代码提供服务;它根本不需要是特殊情况:

int queue_push_back(struct queue* queue, int value){
    if (queue->size >= queue->capacity) {
        return -1;
    } else {
        queue->head[queue->size].value = value;
        return ++queue->size;
    }
    // That's all, folks!
}

【讨论】:

  • 感谢您的反馈。我知道,它不能很好地一次分配所有队列元素。但不幸的是,这是任务中想要的。这是练习的一部分,我需要使用线程、生产者/消费者...。我需要在其中分配容量的队列(节点具有指针)。所以,我必须解决这个问题。
  • @KryptMath,我想我可以更明确地说你应该选择我概述的两种方法中的 一种,并坚持下去。我已经用一些具体的建议更新了这个答案,特别注意你的 queue_push_back() 函数。对于您的出队功能将如何发挥作用留作练习(这是两者中更复杂的一个)。
  • 谢谢。我想有了这个我将能够解决这个问题。感谢您花时间帮助我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-23
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多