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类型结构中?
答案 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()
函数。