为什么我的指针锁定在范围内?

时间:2015-07-06 23:28:00

标签: c pointers memory-management data-structures linked-list

pop功能似乎总是会出现段错误。

当我将指针变量传递给前面的函数时,函数会相应地运行,并且"似乎"成功存储数据。但是,仔细检查后,数据就会丢失和/或损坏。即使是将一个Stack变量初始化为NULL这样的简单事情也似乎失败了。

我知道堆栈已正确分配。我知道数据现在正在存储,但需要注意。我必须将头指针传回我刚刚创建的堆栈(我不喜欢这种方法BTW)。

operators = stack_operators(Token *object);

这导致了我宁愿避免的更多问题和复杂情况。

if (NULL == (operator = stack_operators(&tree)))
{//this code works and succeeds
    puts("failed to stack operators.");
    puts("committing suicide now.");
    break;
}

print_stack(operator);//prints malloc()d stack to screen

同样的问题影响了我的pop()功能。具有讽刺意味的是,我成功释放了分配的堆栈节点,但我的程序拒绝将下一个节点转移到前一个节点,从而导致SIGSEGV错误!这会导致发生双重释放或损坏错误。

我忽略了什么概念以及未来如何避免这些类型的错误?

流行驱动程序

此程序重新创建我的问题,而不会在第143行引起分段错误。线136和140是发生数据丢失的地方。这发生在我的实际程序中,SIGSEGV事件导致双重自由SIGSEGV事件。

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>

/* the Stack structure */
typedef struct stack_t {
    int object;
    struct stack_t * next;
} Stack;

typedef enum precedence_t { none, low, mid, hi } Precedence;  

//pop an item off of the stack
int pop(Stack * stack, int position);
//make a stack for the pop and push functions
Stack * stack_operators(const char ** tree, const int size);
Stack * stack_digits(const char ** tree, const int size);
//print tokens and stacks to screen
void print_token(const char ** tree, const int size);
void print_stack(Stack * stack);

//the token tree
const int SIZE = 4;

const char * data[5] = {
    "-",
    "123",
    "+",
    "54"
};

int main(void)
{
    Stack * digits = NULL, * operators = NULL;
    int value = 0;

    puts("Token List Value...");
    print_token(data, SIZE);

    puts("Converting token list to stack type...");
    puts("making digit stack...");
    digits = stack_digits(data, SIZE);

    puts("printing digit stack to screen:");
    print_stack(digits);

    puts("making operator stack...");
    operators = stack_operators(data, SIZE);

    puts("printing operator stack to screen:");
    print_stack(operators);

    puts("popping digits at position 0...");
    value = pop(digits, 0);
    printf("value = %d\n", value);

    puts("popping digits at position 0...");
    value = pop(digits, 0);
    printf("value = %d\n", value);

    puts("popping digits at position 0...");
    value = pop(digits, 0);//this is where the problem occurs
    printf("value = %d\n", value);

    return(0);
}

void print_token(const char ** tree, const int size)
{//print tokens to screen
    if (0 == size)
    {
        printf("the token tree has nothing to print.");
    }

    for (int count = 0; count < size; count++)
    {
        printf("token %0d: '%s'\n", (count + 1), tree[count]); 
    }

    putchar('\n');
}

void print_stack(Stack * stack)
{//print stack values to screen
    Stack * head = stack;

    if (NULL == stack)
    {
        puts("nothing in the stack to print.");
    }

    for (int i = 0; NULL != stack; i++)
    {
        if (ispunct(stack->object))
        {
            printf("stack: %d | value: '%c'\n", i, stack->object);
        }
        else
        {
            printf("stack: %d | value: '%d'\n", i, stack->object);
        }

        stack = stack->next;
    }

    stack = head;

    putchar('\n');
}

//Stack is the stack structure
//position is the location of the element to be popped
int pop(Stack * stack, const int position)
{//pop an object from the stack, free it, and return the its value
    int pos;
    int data = 0;
    Stack * previous = NULL;
    Stack * dump = NULL;
    Stack * head = stack;

    for (pos = 0; NULL != stack; pos++)
    {   
        if (pos != position)
        {
            previous = stack;
            stack = stack->next;
            continue;
        }

        data = stack->object;
        dump = stack;

        if (NULL == previous)
        {//first object on the stack
            stack = stack->next;//this line is supposed to cause a SEGFAULT
        }
        else
        {
            previous->next = stack->next; //stack does not retain pointer value
        }

        free(dump);//same stack is attempted to be freed, double free SIGSEGV

        break;      
    }

    if (0 < pos) { stack = head; }

    return data;
}

static Precedence token_precedence(char operator)
{//returns operator precedence
    switch (operator)
    {
        case '*':
        case '/':
            return mid; 
        case '+':
        case '-':
            return low;
        default:
            return none;
    }
}

//determines whether operator is unary or not
static bool isunary(const char ** token, int position)
{   //do NOT allow more than 2 consecutive + or - tokens
    //if there are, consider it to be a violation
    //if the last token is an operator, consider it to be a violation
    int previous = position - 1;
    Precedence precedence;

    if (0 == position)
    {
        if ('-' == token[position][0])
        {
            return true;
        }
    }

    if (previous <= 0) 
    {//too early to scan backwards
        return false;
    }

    precedence = token_precedence(token[previous][0]);

    if (low == precedence || mid == precedence)
    {
        if ('-' == token[position][0])
        {
            return true;
        }
    }

    return false;
}

//makes a stack for the given operators
Stack * stack_operators(const char ** tree, const int size)
{//initialize each stack with a value to be processed
    Stack * previous, * current, * head = NULL;

    for (int leaf = 0; leaf < size; leaf++)
    {
        if (!ispunct(tree[leaf][0]))
        {   
            continue;
        }

        if (isunary(tree, leaf))
        {
            continue;
        }

        if (NULL == (current = malloc(sizeof(Stack))))
        {//failed to allocate memory for the stack
            return NULL;
        }

        if (NULL == head)
        {//point to the head of the linked list
            head = current;
        }
        else
        {
            previous->next = current;
        }

        current->next = NULL;
        current->object = tree[leaf][0];
        previous = current;
    }

    return head;
}

//makes a stack for the given digits
Stack * stack_digits(const char ** tree, const int size)
{//initialize each stack with a value to be processed
    Stack * previous, * current, * head = NULL;

    for (int leaf = 0; leaf < size; leaf++)
    {
        if (ispunct(tree[leaf][0]))
        {
            if (!isunary(tree, leaf))
            {
                continue;
            }
        }

        if (NULL == (current = malloc(sizeof(Stack))))
        {//failed to allocate memory for the stack
            return NULL;
        }

        if (NULL == head)
        {
            head = current;
        }
        else
        {
            previous->next = current;
        }

        if (isunary(tree, leaf))
        {
            ++leaf;
            current->object = -(atoi(tree[leaf]));
        }
        else
        {
            current->object = atoi(tree[leaf]);
        }

        current->next = NULL;
        previous = current;
    }

    return head;
}

是什么让我相信这很奇怪,是因为我的印象是指针应该允许我&#34;弯曲&#34;特定值的范围,以便我最终能以某种方式改变或影响它。

以下代码片段的有趣之处在于stack->next->object非空,并且可能会将下一个指针指定给当前堆栈项,但却无法执行此操作。为什么会这样?

if (NULL == previous)
{//first object on the stack
    puts("in pop(), previous IS null...");
    if (NULL == stack->next)
    {
        puts("next stack IS null...");
    }
    else
    {
        printf("stack next object: %d\n", stack->next->object);
    }
    stack = stack->next;
}

这就是为什么程序员通常会使用类似结构类型的Item并将其包装在代表链表的Node类型结构中?

2 个答案:

答案 0 :(得分:4)

    if (0 < pos) { stack = head; }

该函数末尾的这一行实际上没有做太多。您将指向数据的指针传递为stack,但在C中,此指针本身将按值复制。这意味着当数据指向同一个点时,指针本身就是不同的东西。在函数结束时,您将修改此复制的值,并且从函数返回时更改将丢失。如果要修改指针,则必须将指针传递给指针,如Stack **stack,然后使用

进行修改
    if (0 < pos) { *stack = head; }

请记住,C中的所有内容都是按值传递的,因此如果要修改任何内容(包括指针),请使用指向这些值的指针。

我不确定这是否能回答你的问题,但这条线似乎没有做太多。

答案 1 :(得分:3)

根据您的Stack类型,您的推送和弹出功能可能是:

void push(Stack **s, int value)
{
    assert(s != 0);
    Stack *node = malloc(sizeof(*node));
    if (node == 0) { …handle memory allocation error… }
    node->next = *s;
    node->object = value;
    *s = node;
}

bool pop(Stack **s, int *value)
{
    assert(s != 0 && value != 0);
    if (*s == 0)
        return false;
    else
    {
        Stack *node = *s;
        *s = node->next;
        *value = node->object;
        free(node);
        return true;
    }
}

然后在main()中,您可以使用:

Stack *operands = 0;
push(&operands, 123);
push(&operands, 456);
push(&operands, 789);

int value;
while (pop(&operands, &value))
    printf("Popped: %d\n", value);
return(0);

将所有这些放在一起,我得到:

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

/* For header */
typedef struct Stack Stack;

void push(Stack **s, int value);
bool pop(Stack **s, int *value);

/* Implementation only */
struct Stack
{
    int object;
    Stack *next;
};

void push(Stack **s, int value)
{
    assert(s != 0);
    Stack *node = malloc(sizeof(*node));
    if (node == 0)
    {
        fprintf(stderr, "Out of memory!\n");
        exit(1);
    }
    node->next = *s;
    node->object = value;
    *s = node;
}

bool pop(Stack **s, int *value)
{
    assert(s != 0 && value != 0);
    if (*s == 0)
        return false;
    else
    {
        Stack *node = *s;
        *s = node->next;
        *value = node->object;
        free(node);
        return true;
    }
}

int main(void)
{
    Stack *operands = 0;

    push(&operands, 123);
    push(&operands, 456);
    push(&operands, 789);

    int value;
    while (pop(&operands, &value))
        printf("Popped: %d\n", value);

    return(0);
}

来自运行的输出:

Popped: 789
Popped: 456
Popped: 123

valgrind下运行它也可以给它一个干净的健康状况。还有其他处理内存不足情况的方法。

您可能还注意到描述堆栈接口的标头不需要结构类型的详细信息;可以保持隐藏,以便实现了解(仅)。这是一种封装形式。您可以将示例代码拆分为三个文件:stack.h包含typedef和两个函数声明; stack.c包括stack.h,仅包含两个函数定义;和main.c包括stack.h,仅包含main()函数。