【问题标题】:Pointer Dequeue - pointer trainingPointer Dequeue - 指针训练
【发布时间】:2016-10-09 18:41:20
【问题描述】:

我正在尝试在 C 中设置一个双端指针队列。 到目前为止,我已经完成了推送功能的工作和测试。我的问题似乎是在两端弹出条目。

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

dequeue* dequeue_create()
{
    return NULL;
}
void dequeue_push_front(dequeue** dq, int data)
{
    dequeue* tmp = malloc(sizeof(*tmp));
    tmp->data = data;
    tmp->next=NULL;
    if((*dq) == NULL)
    {
        (*dq) = tmp;
    }
    else
    {
        if ((*dq)->next == NULL)
        {
            (*dq)->next = tmp;
            tmp->prev   = (*dq);

        }
        else
        {

            dequeue* tmp_it  = malloc(sizeof(struct _dequeue_));
            tmp_it = (*dq)->next;
            while(tmp_it->next != NULL)
            {
                tmp_it = tmp_it->next;

            }
            tmp_it->next = tmp;
            tmp->prev    = tmp_it;

        }
    }
}

void dequeue_push_back(dequeue** dq, int data)
{
    dequeue* tmp = malloc(sizeof(struct _dequeue_));
    tmp->data = data;
    tmp->prev=NULL;
    if((*dq) == NULL)
    {
        (*dq) = tmp;
    }
    else
    {

        if ((*dq)->prev == NULL)
        {
            (*dq)->prev = tmp;
            tmp->next   = (*dq);

        }
        else
        {
            dequeue* tmp_it  = malloc(sizeof(struct _dequeue_));
            tmp_it = (*dq)->prev;
            while(tmp_it->prev != NULL)
            {
                tmp_it = tmp_it->prev;
            }
            tmp_it->prev = tmp;
            tmp->next    = tmp_it;

        }
    }
}

int dequeue_pop_front(dequeue** dq)
{
    dequeue* tmp_get = malloc(sizeof(struct _dequeue_));
    int output = 0;

    if((*dq)->next == NULL)
    {
        printf("\ndqnext==null\n");
    }
    else
    {
        tmp_get = (*dq);
        while(tmp_get->next != NULL)
        {
            tmp_get= tmp_get->next;
            output = tmp_get->data;
        }
        tmp_get=tmp_get->prev;
        free(tmp_get->next);
        tmp_get->next=NULL;
    }
    return output;
}

int dequeue_pop_back(dequeue** dq)
{
    dequeue* tmp_get = malloc(sizeof(struct _dequeue_));
    int output = 0;

    if((*dq)->prev == NULL)
    {
        printf("\ndqprev==null\n");
    }
    else
    {
        tmp_get = (*dq);
        while(tmp_get->prev != NULL)
        {

            output = tmp_get->data;
            tmp_get= tmp_get->prev;

        }
        free(tmp_get);
        tmp_get=NULL;
    }
    return output;
}

dequeue.h:

#ifndef dequeue_H
#define dequeue_H

struct _dequeue_ {
  struct _dequeue_* next;
  struct _dequeue_* prev;
  int data;
};

typedef struct _dequeue_ dequeue;

dequeue* dequeue_create();
void dequeue_destroy(dequeue** queue);

int dequeue_pop_front(dequeue** dq);
void dequeue_push_front(dequeue** dq, int data);

int dequeue_pop_back(dequeue** dq);
void dequeue_push_back(dequeue** dq, int data);

#endif  /* dequeue_H */

main.c:

int main()
{
    dequeue* dq = dequeue_create();

    dequeue_push_front(&dq, 1);
    dequeue_push_back(&dq, 2);
    dequeue_push_front(&dq, 3);
    for (int cnt = 1; cnt <=4; cnt++)
    {
        printf("FINAL=%d ", dequeue_pop_front(&dq));
    }


    //TODO : dequeue_destroy(&dq);

    return 0;
}

我对指针很陌生,这似乎是我的问题。

我在弹出函数中尝试做的是遍历指针以到达最后一个并释放最后一个指针。但似乎它没有释放指针。现在尝试了几种不同的方法,但似乎都不起作用,难道是我设置推送功能的方式无法释放指针?

非常感谢任何帮助。 干杯

【问题讨论】:

  • 我不明白你为什么在 pop 中调用 malloc()。这会导致代码中的内存泄漏。
  • 我考虑过使用 tmp_get 作为 tmp 指针。将此修复为 dequeue* tmp_get = (*dq);并在 elses 中将其删除。
  • 支持(几乎)stackoverflow.com/help/mcve

标签: c pointers


【解决方案1】:

在深入研究实际问题之前的一些提示:

  • 在dequeue.c 中,第一个#include 应该是dequeue.h 的#include,因为这是确保dequeue.h 包含自身所需的一切的系统方法。
  • 结构dequeue 应该被称为dequeue。 (后面的typedef可以使用同名——这里没问题)

然后,有一些误解:您的 _dequeue_s 是 dequeue 的节点,但不是 dequeue 本身。所以,你应该有两个结构:

struct dequeue_node {
        struct dequeue_node * next;
        struct dequeue_node * prev;
        int data;
};

struct dequeue {
        struct dequeue_node * frst;
        struct dequeue_node * last;
        size_t size;
};

首先调用条目(而不是第一个)是我的个人风格,它使它像 next/prev/last/size 一样长 4 个字符,但如果您愿意,可以先调用它。 size 不是必需的,但允许 O(1) 检索出队的大小。

这样,您不必遍历整个 dequeue 即可找到它的结尾。

所以,现在你的实际问题是:

在创建第一个节点时,dequeue_push_front 不会初始化 tmp->prev(并且 dequeue_push_back 不会初始化 tmp->next)。所以,你的出队从一开始就处于非法状态。

然后,当我想到“前”时,我会想到“第一”并假设有下一个。所以,几乎,你正在交换 next 和 prev 的含义。假设这个:

                       a               b               c
                +-------------+ +-------------+ +-------------+
                | next = b    | | next = c    | | next = NULL |
                | prev = NULL | | prev = a    | | prev = b    |
                +-------------+ +-------------+ +-------------+

我会称a“第一个”和c“最后一个”。但是 dequeue_push_front 尝试在这个 ascii 艺术中添加 c 右边的一个元素。

鉴于您正在命名,推送功能似乎是正确的(除了上述几点)。

pop 函数现在有一个 s.b 已注意到的错误。谁再次删除了他的帖子 Ben Wainwright(他编辑了,显示为帖子删除)。你检查 (*dp)->next/prev == NULL 如果是真的,你就退出,但你应该删除节点。所以对于 dequeue_pop_front (在你的实现中):

if ((*dq)->next == NULL) {
    dequeue * tmp = (*dq);
    output = tmp->data;
    (*dq) = (*dq)->prev;
    if ((*dq)) {
            (*dq)->next = NULL;
    }
    free(tmp);
} else {
    ...

在 pop_back 中反之亦然。

剩下的问题是效率低下和内存泄漏,对于后者你应该学会使用http://valgrind.org/来找到它们,而对于第一个,你应该经常问自己,weather a statement can be move out of a loop ,就像这里:(再次来自 dequeue_pop_front):

    while(tmp_get->next != NULL)
    {
        tmp_get= tmp_get->next;
        output = tmp_get->data; // this can be moved out
    }

输出变量会不断被覆盖,直到循环结束,所以,把它放掉:

    while(tmp_get->next != NULL)
    {
        tmp_get= tmp_get->next;
    }
    output = tmp_get->data; // this can be moved out

所以,最终确定的 dequeue.c(但你真的应该使用 dequeue 和 dequeue_node 进行更改):

dequeue* dequeue_create()
{
    return NULL;
}
void dequeue_push_front(dequeue** dq, int data)
{
    dequeue* tmp = malloc(sizeof(*tmp));
    tmp->data = data;
    tmp->next=NULL;
    tmp->prev=NULL;
    if((*dq) == NULL)
    {
        (*dq) = tmp;
    }
    else
    {
        if ((*dq)->next == NULL)
        {
            (*dq)->next = tmp;
            tmp->prev   = (*dq);

        }
        else
        {

            dequeue* tmp_it  = malloc(sizeof(struct _dequeue_));
            tmp_it = (*dq)->next;
            while(tmp_it->next != NULL)
            {
                tmp_it = tmp_it->next;

            }
            tmp_it->next = tmp;
            tmp->prev    = tmp_it;

        }
    }
}

void dequeue_push_back(dequeue** dq, int data)
{
    dequeue* tmp = malloc(sizeof(struct _dequeue_));
    tmp->data = data;
    tmp->next=NULL;
    tmp->prev=NULL;
    if((*dq) == NULL)
    {
        (*dq) = tmp;
    }
    else
    {

        if ((*dq)->prev == NULL)
        {
            (*dq)->prev = tmp;
            tmp->next   = (*dq);

        }
        else
        {
            dequeue* tmp_it  = malloc(sizeof(struct _dequeue_));
            tmp_it = (*dq)->prev;
            while(tmp_it->prev != NULL)
            {
                tmp_it = tmp_it->prev;
            }
            tmp_it->prev = tmp;
            tmp->next    = tmp_it;

        }
    }
}

int dequeue_pop_front(dequeue** dq)
{
    dequeue* tmp_get = malloc(sizeof(struct _dequeue_));
    int output = 0;

    if((*dq)->next == NULL)
    {
        dequeue * tmp = (*dq);
        output = tmp->data;
        (*dq) = (*dq)->prev;
        if ((*dq)) {
                (*dq)->next = NULL;
        }
        free(tmp);
    }
    else
    {
        tmp_get = (*dq);
        while(tmp_get->next != NULL)
        {
            tmp_get= tmp_get->next;
            output = tmp_get->data;
        }
        tmp_get=tmp_get->prev;
        free(tmp_get->next);
        tmp_get->next=NULL;
    }
    return output;
}

int dequeue_pop_back(dequeue** dq)
{
    dequeue* tmp_get = malloc(sizeof(struct _dequeue_));
    int output = 0;

    if((*dq)->prev == NULL)
    {
        dequeue * tmp = (*dq);
        output = tmp->data;
        (*dq) = (*dq)->next;
        if ((*dq)) {
                (*dq)->prev = NULL;
        }
        free(tmp);
    }
    else
    {
        tmp_get = (*dq);
        while(tmp_get->prev != NULL)
        {

            output = tmp_get->data;
            tmp_get= tmp_get->prev;

        }
        free(tmp_get);
        tmp_get=NULL;
    }
    return output;
}

【讨论】:

    【解决方案2】:

    推动

    dequeue_push_front(&dq, 1);
    

    NULL NULL

    dq 指向中间节点

    dequeue_push_front(&dq, 2);
    

    NULL 右 (2) -> NULL

    dq 仍然指向中间节点,因为您的代码只有在 dqNULL 的形式传入时才会更改它

    NULL 中 (1) 右 (2) -> NULL

    dq仍然出于同样的原因指向中间节点

    流行音乐

    让我们跟随你的流行代码:

     tmp_get = (*dq);
     while(tmp_get->next != NULL)
     {
         tmp_get= tmp_get->next;
         output = tmp_get->data;
     }
     tmp_get=tmp_get->prev;
     free(tmp_get->next);
     tmp_get->next=NULL;
    

    这会导致以下状态:

    NULL 中间 (1) -> NULL

    dq 仍然指向中间节点。下一次,每隔一次执行函数

    if((*dq)->next == NULL)
    

    将评估为 true,不会导致进一步的更改。

    其他问题

    如 cmets 中所述,您的 pop 函数内部存在内存泄漏。见下文:

    dequeue* tmp_get = malloc(sizeof(struct _dequeue_));
    ...
    tmp_get = (*dq);
    

    malloc() 被调用,分配sizeof(struct _dequeue_) 字节的内存。返回一个指针,并分配给变量 tmp_get。然后,您用不同的指针覆盖此指针,这意味着现在有一块您无法访问或释放的已分配内存。我看不出你为什么需要首先分配这个内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-15
      • 1970-01-01
      • 2017-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多