【问题标题】:C Program Segmentation FaultC 程序分段错误
【发布时间】:2015-02-12 09:02:13
【问题描述】:

我很难在类项目的一些代码中定位分段错误(这部分未分级)。我正在为一个 OS 类实现一个队列,并且在 add 函数中遇到了分段错误。

void AddQueue(QElem * head, QElem * item) {
    printf("WHERE\n");
    if(head == NULL){
        printf("THE\n");
        head = item;
        //item->next = item;
        //item->prev = item;
    }
    else{
        printf("$^&*\n");
        (head->prev)->next = item;
        printf("ARE\n");
        item->prev = (head->prev);
        printf("YOU\n");
        item->next = head;
        printf("FAILING\n");
        head->prev = item;
    }
    printf("!?!?!?\n");
}

我有一个从不同类调用的测试函数...

void TestAddQueue()
{
    printf("********************************************\n");
    printf("Begin testing the add test function\n");
    printf("********************************************\n");

    QElem * queue;
    InitQueue(queue);


    for(int i = 0; i < 10; i++)
    {
        printf("Adding element %d\n", i+1);
        QElem * newElem = NewItem();
        printf("Changing payload value\n");
        newElem->payload = i+100;
        printf("Adding to the queue\n");
        AddQueue(queue, newElem);
        printf("Item added, payload value = %d\n", queue->payload);
        printf("The previous payload = %d\n", queue->prev->payload);

    }
    for(int i = 0; i < 10; i++)
    {
        printf("Rotating list", i+1);
        RotateQ(queue);
        printf("Printing element %d\n", i+1);
        printQElem(queue);
    }
}

这里是 NewItem 函数...

QElem * NewItem()
{
    // just return a new QElem struct pointer on the heap
    QElem * newItem = calloc(1,sizeof(QElem));
    newItem->next = newItem;
    newItem->prev = newItem;
    newItem->payload = -1;
    return newItem;
}

...这是运行程序的输出...

********************************************
Begin testing the add test function
********************************************
Adding element 1
Changing payload value
Adding to the queue
WHERE
THE
!?!?!?
Segmentation fault

现在传递给 add 函数的头指针应该是 NULL,因为它发送到一个初始化函数,该函数只是将指针的值设置为 NULL,所以我不认为这会导致我的问题。

我的猜测是下一行是导致问题的原因...

printf("Item added, payload value = %d\n", queue->payload);

可能当我尝试获取有效负载值时,我尝试访问的结构不再存在,或者队列指针以某种方式移动到了无效空间。任何朝着正确方向的反馈或轻推将不胜感激。

旁注:这是在 Unix 服务器环境 (bash) 中编译的,目前我无法访问 IDE 来调试和查看变量。

【问题讨论】:

  • 您的示例中缺少很多代码;以 NewItem() 为例。也就是说,我说:查看 AddItem 中的“(head->prev)->next”行:您已检查“head”是否为非 NULL,但尚未检查“head->prev”是否为非空

标签: c pointers segmentation-fault


【解决方案1】:

在 C 中,参数是按值传递的,这意味着它们是复制的。更改副本当然不会更改原件。

所以在AddQueue 函数中,变量head 是一个副本,你可以随心所欲地改变,你最初传递给函数的变量根本不会改变。

为了能够更改参数,您需要按引用传递,C 没有,但可以使用指针来模拟。这当然意味着要通过引用传递指针,您必须将指针传递给指针。


所以对于你的代码,它会是这样的

void AddQueue(QElem ** head, QElem * item) {
    if(*head == NULL){
        *head = item;
    }
    ...
}

...

AddQueue(&queue, newElem);

上述更改首先使AddQueue 获取指向QElem 的指针,从而使其模拟传递引用习语。要使用原始指针,请使用取消引用运算符*,它为您提供指针指向的值(在本例中为原始指针)。然后要实际将指针传递给指针,您必须在指针变量上使用地址运算符&amp;

【讨论】:

  • 谢谢@Joachim 感谢您提供的信息和示例。我花了一些时间来寻找和重构,但我能够让我的代码按预期工作。我还发现了这个很棒的网站link。我将添加适用于我的答案的重构代码。再次感谢,干杯!
【解决方案2】:

head = itemAddQueue 函数之外发生的事情没有任何影响。如果您将空指针作为head 传递给AddQueue,则在AddQueue 完成后,该指针仍将为空。

【讨论】:

    【解决方案3】:

    感谢@Joachim,我能够让我的队列按预期运行。请参阅下面的重构代码。

    首先添加函数...

    ////////////////////////////////////////////////////////////////////////////////
    //
    //  Add Queue
    //
    //      Adds a queue item, pointed to by `item`, to the end of the queue pointed
    //      to by `head`.
    //
    //      Note: Tested 2-12-2015 using proj_1.c tests. PASSED -Dave
    //
    ////////////////////////////////////////////////////////////////////////////////
    int AddQueue(QElem ** head, QElem ** item) {
    
        //If the queue is empty...
        if(*head == NULL){
            //Point the head to the item.  The new item's next/prev were initialized
            //to point to itself already.
            *head = *item;
        }
        //If there are already elements in the queue...
        else{
            // insert the new element at the end of the list (just to the left
            // of the head) setting the next and previous values of the
            // appropriate nodes to the new values.
            ((*head)->prev)->next = *item;
            (*item)->prev = ((*head)->prev);
            (*item)->next = *head;
            (*head)->prev = *item;
        }
        return TRUE;
    }
    

    接下来是新的物品功能...

    ////////////////////////////////////////////////////////////////////////////////
    //
    //  New Item
    //
    //      Returns a pointer to a new queue element created in heap memory.
    //
    //      Note: Calloc is a more precise way of allocating, but is basically the
    //      same as malloc, the 1 denotes how many of the item to reserve mem for.
    //      -Dave
    //
    //      Note: Tested 2-12-2015 using proj_1.c tests. PASSED -Dave
    //
    ////////////////////////////////////////////////////////////////////////////////
    QElem * NewItem()
    {
        // just return a new QElem struct pointer on the heap with values initialized
        QElem * newItem = calloc(1,sizeof(QElem));
        newItem->next = newItem;
        newItem->prev = newItem;
        newItem->payload = -1;
        return newItem;
    }
    

    现在为测试添加功能...

    ////////////////////////////////////////////////////////////////////////////////
    //
    //  A test function for the add function.  It will create a queue of items
    //  and attempt to iterate through them and print the value of the payload
    //
    ////////////////////////////////////////////////////////////////////////////////
    void TestAddQueue(QElem ** queue){
        printf("********************************************\n");
        printf("Begin testing the add test function\n");
        printf("********************************************\n");
    
        InitQueue(&(*queue));
    
        for(int i = 0; i < 10; i++)
        {
            printf("Adding element %d\n", i+1);
            QElem * newElem = NewItem();
            printf("Changing payload value\n");
            newElem->payload = i+100;
            printf("Adding to the queue\n");
            AddQueue(&(*queue), &newElem);
            printf("Item added, payload value = %d\n", newElem->payload);
            printf("The previous payload = %d\n", (*queue)->prev->prev->payload);
    
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-01-08
      • 2015-06-14
      • 1970-01-01
      • 2012-10-12
      • 2018-11-18
      • 2011-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多