为什么我的代码出现了段错误?应该简单吗?

时间:2011-11-03 03:17:26

标签: c segmentation-fault

我完全失去了。我觉得这里肯定会有一些明显的,愚蠢的错误,我的眼睛太累了,看不到它。我真的很感激任何帮助。

当我运行testProb2时,程序打印“Pushing:”然后在下一行“Segmentation fault”。我觉得这很奇怪,因为接下来就是printf(“Pushing:\ n”);是对printf的另一个调用,只传递了一个静态参数,没有任何动态可以在其他方法中做出奇怪的疯狂事情,但是“1”却没有打印出来。

我将printf的调用1和2放在那里作为测试,因为我最初认为问题可能出现在我的第一个for循环中,现在已经注释掉了,但事实并非如此。正如我所说的,我认为问题出现在testProb2.c中,但我在其下面包含了stackli.h和stackli.c以防万一。我用“gcc -ansi stackli.c testProb2.c -o testProb2”编译它。

/* testProb2
 * 
 * Demonstrates a stack implementation that allocates a number of nodes at creation of the stack
 * rather than on each call to Push. All methods are O(1) except for GrowFreeList, which is O(n),
 * and CreateStack, which is O(n) because it calls GrowFreeList, and possibly Push, which will be
 * O(1) when called while there are empty nodes, but O(n) when called if there are not empty nodes.
 */

#include "stackli.h"
#include <stdio.h>

int main(void) {
    Stack S;
    int i;

    S = CreateStack(8);

    printf("Pushing:\n");
    printf("1 ");
    Push(1, S);
    printf("2\n");
    Push(2, S);
                /*
                for (i = 0; i < 10; i++) {
                    printf("%d...", i);
                    Push(i, S);
                }
                printf("]\n");
                */

    printf("Popping:\n");
    while (!IsEmpty(S)) {
        printf("%d...", Top(S));
        Pop(S);
    }
    printf("]");
    DisposeStack(S);
}
/* stackli.h */

    typedef int ElementType;

    #ifndef _Stack_h
    #define _Stack_h

    struct Node;
    struct StackRecord;
    typedef struct Node *PtrToNode;
    typedef struct StackRecord *Stack;

    Stack CreateStack( int initialSize );
    void GrowFreeList( Stack S );
    int IsEmpty( Stack S );
    int IsFull( Stack S ) ;
    void MakeEmpty( Stack S );
    void DisposeStack( Stack S );
    void Push( ElementType X, Stack S );
    ElementType Top( Stack S );
    void Pop( Stack S );


    #endif  /* _Stack_h */
/* stackli.c */

#include "stackli.h"
#include "fatal.h"
#include <stdlib.h>

struct StackRecord {
    PtrToNode ThisStack;
    PtrToNode FreeNodes;
    int Size;
};

struct Node {
    ElementType Element;
    PtrToNode   Next;
};

/* O(n) instead of O(1) because it calls GrowFreeList which is O(n) */
Stack CreateStack(int initialSize) {
    Stack S;
    S = malloc( sizeof( struct StackRecord ) );
    S->ThisStack = NULL;
    S->FreeNodes = NULL;
    S->Size = initialSize;            
    GrowFreeList(S);
    return S;
}

/* O(n) function */
void GrowFreeList(Stack S) {
    int i;
    PtrToNode temp;

    for (i = 0; i < S->Size; i++) {
        temp = malloc( sizeof( struct Node) );
        if (temp == NULL)
            FatalError("Out of space!!");
        temp->Next = S->FreeNodes;
        S->FreeNodes = temp;
    }
    S->Size = S->Size * 2;
}

4 个答案:

答案 0 :(得分:4)

问题出现在Push的最后两行:

void Push( ElementType X, Stack S ) {
    PtrToNode temp;
    ...
    temp->Next = S->ThisStack->Next;
    S->ThisStack = temp;
}

当您第一次拨打Push时,ThisStack字段为空。当您尝试取消引用它以访问其Next字段时,您将遇到段错误。但是,由于堆栈的顶部位于ThisStack,而不是ThisStack->next,因此修复该问题将消除段错误。

void Push( ElementType X, Stack S ) {
    PtrToNode temp;
    ...
    temp->Next = S->ThisStack;
    S->ThisStack = temp;
}

你在Pop犯了同样的错误,这会导致你完全跳过第一个元素。 temp的分配应该是这样的:

temp = S->ThisStack;

最后,您的Size字段始终是错误的。似乎GrowFreeList在调用时应该使堆栈的大小加倍,但是当您从CreateStack调用它时,尽管Size字段为8,但堆栈的实际大小为0 (在你的例子中)。结果是堆栈包含8个空闲节点,但其Size字段为16.实际上,Size字段将始终比初始大小的实际大小大。这不会导致任何问题,因为它仅用于确定要添加的数量,但修复很简单:从Size调用GrowFreeList后重置CreateStack字段:< / p>

Stack CreateStack(int initialSize) {
    Stack S;
    ...
    S->Size = initialSize;
    GrowFreeList(S);
    S->Size = initialSize; // Reset size, since GrowFreeList changed it
    return S;
}

答案 1 :(得分:2)

Okey doke ......我们走了:

您使用Stack S初始化CreateStack(8)。这会在结构中将ThisStack设置为NULL。那么你要做的第一件事就是调用Push(1,S)来做...

temp->Next = S->ThisStack->Next;

KABOOM。您刚刚取消引用NULL指针。

理想情况下,您希望熟悉使用调试器(gdb)。这将让您逐步执行代码,看看它到底在哪里爆炸。

答案 2 :(得分:2)

正如其他人所说,您应该使用fprintf(stderr,...)来打印调试消息。

您可以使用gdb查看问题所在,并查看内存中发生的情况。在编译时添加标志-g以帮助在gdb中进行调试。

这是gdb给我的输出:

(gdb) r
Starting program: .../test 
Pushing:
1 2
2

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400873 in Push (X=1, S=0x601010) at stackli.c:81
81      temp->Next = S->ThisStack->Next;
(gdb) bt
#0  0x0000000000400873 in Push (X=1, S=0x601010) at stackli.c:81
#1  0x0000000000400627 in main () at testprob2.c:20
(gdb) print temp
$1 = (PtrToNode) 0x601110
(gdb) print S->ThisStack
$2 = (PtrToNode) 0x0

错误发生在void Push( ElementType X, Stack S )

正如您在我们打印S->ThisStack时所看到的那样,我们得到0x0,即。 S->ThisStack指向NULL。当您取消引用S->ThisStack以转到S->ThisStack->Next时,您将遇到分段错误。

您可以为if (S->ThisStack == NULL)添加支票。我不太确定你要创建堆栈结构的方式。

另请注意,这只是您的代码的一个问题,可能(并且很可能)有更多问题,但是您应该能够使用backtrace({{1}使用gdb缩小它们的范围。命令,并打印到bt而不是stderr,以便您的输出不被缓冲。我不想为你做功课。

答案 3 :(得分:1)

关于冲洗的评论是正确的。问题出现在Push的倒数第二行。您从未初始化ThisStack,因此当您尝试访问其next元素时,您会遇到段错误。顺便提一下,你的尺寸是关闭的,因为你在第一次填充它后加倍。最后,您应该尽量避免typedef远离指针。很容易忘记什么是指针,什么不是。使堆栈成为完整的结构并声明指向它的指针。