我想在C中创建一个堆栈数组,在那里我应该能够保留单个堆栈及其各自的信息。我目前有以下实现,它只适用于一个堆栈。如何修改push和pop函数以实现多个堆栈,每个堆栈使用相同的功能。 (我很容易用Java做到这一点,因为我可以创建一个类,但我不知道在C中)
#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *next;
};
struct node *first = NULL;
void push(int x) {
struct node *newnode = malloc(sizeof(struct node));
newnode->data = x;
newnode->next = first;
first = newnode;
}
int pop() {
int temp = first->data;
first = first->next;
return temp;
}
答案 0 :(得分:2)
pop()
函数中的代码中存在内存泄漏。你应该释放你拥有malloc'd的记忆。
从@ Jongware的评论中获取建议。
以下是push()
和pop()
函数的新版本。
#include <stdlib.h>
struct node {
int data;
struct node *prev;
};
void push(struct node **stack, int x) {
if (stack != NULL)
{
struct node *newnode = malloc(sizeof(struct node));
newnode->data = x;
newnode->prev = *stack;
*stack = newnode;
} else
{
// You didn't give me a valid pointer to a stack, so I'm ignoring you!
}
}
int pop(struct node **stack) {
int temp = 0; // This is the default value that is returned when there is an error.
struct node *oldnode;
if (stack != NULL)
{
if (*stack != NULL)
{
oldnode= *stack;
temp = oldnode->data;
(*stack) = oldnode->prev;
free(oldnode);
} else
{
// The stack is empty. I will just ignore you and return the default value for temp.
}
} else
{
// You didn't give me a valid pointer to a stack so I'm ignoring you and returning the default value of 0 for temp!
}
return temp;
}
以下是如何使用它们的示例:
#include <stdio.h>
int main()
{
struct node *stack1 = NULL, *stack2 = NULL;
int value;
// Push some values onto the stacks
printf("Pushing 7 and then 8 onto stack1\n");
push(&stack1, 7);
push(&stack1, 8);
printf("Pushing 3 onto stack2\n");
push(&stack2, 3);
// Pop and print both stacks
value = pop(&stack2);
printf("Popped %d from stack2\n", value);
value = pop(&stack1);
printf("Popped %d from stack1\n", value);
value = pop(&stack1);
printf("Popped %d from stack1\n", value);
return 0;
}
至于,其中你应该声明你的堆栈指针,这取决于你和你打算如何使用它们。
请阅读C variable scope了解某些选项以及如何使用它们。
另外,我必须在函数内包含一个警告,声明这些指针。在您声明指针的任何函数中,您必须确保在退出函数之前弹出 所有,否则您将丢失指针并泄漏所有已分配的内存。如果这不是您想要的,或者您希望指针比该函数更长,那么您可以全局声明指针或传递它,确保在程序存在之前所有内容都从堆栈中弹出或丢失指针。
您可能想要考虑的另一件事是在空堆栈上使用pop()
时会发生什么?我给你的实现只返回0
而忽略了你。你可能想要更好地处理它。
答案 1 :(得分:2)
您只能拥有一个堆栈,因为您将其定义为全局变量:
struct node *first = NULL;
在Java中你会使用一个类。在C中,您也可以通过定义保存实例变量的抽象数据类型来执行“基于对象”的编程,而不是使用全局变量:
struct stack {
struct node *first;
};
没有像构造函数或析构函数这样的类功能,所以你编写函数来初始化堆栈,销毁堆栈等等。要实现多实例化,请将stack *
参数显式传递给堆栈模块中的每个函数。
您可能希望以一致的方式命名您的函数,例如stack_init
,stack_cleanup
,stack_push
等等。
有一些设计问题需要解决,例如:调用者是否会为struct stack
分配stack_init
功能?或者你提供一个分配并返回堆栈的一步stack_alloc
函数?或者两者兼而有之,用户可以选择性能还是方便?
void stack_init(struct stack *);
void stack_cleanup(struct stack *);
struct stack *stack_alloc(void); /* also calls stack_init on new stack */
void stack_free(struct stack *); /* calls stack_cleanup, then frees */
可以在C中隐藏信息,从而可以完全隐藏客户端代码(使用堆栈模块)struct stack
。
但是,如果您提供stack_init
,那么客户端必须知道堆栈的大小,因为它为它提供了内存。通常,完全隐藏实现的模块也会隐藏它的大小,因此只提供stack_alloc
和stack_free
类型的接口。
这样做的一个优点是,如果更改堆栈模块并且结构更大,则不必重新编译客户端代码。如果您正在编写一个广泛使用的库,这是非常好的:用户可以轻松升级或降级。
然而,揭示实现允许更高效的代码,因为客户端可以自由选择堆栈的内存管理。堆栈可以在自动存储中声明为局部变量(“在堆栈中”,可以这么说),静态地表示为全局变量,或者打包到数组中。
用户可以执行以下操作:
{
struct stack temp_stack;
stack_init(&temp_stack); /* stack is good to go! */
/* ... use stack ... */
stack_cleanup(&temp_stack); /* don't forget to clean up */
}
等等:
struct stack array_of_stacks[42];
int i;
for (i = 0; i < 42; i++)
stack_init(&array_of_stacks[i]); /* no memory allocation taking place */
所有这些代码都具有struct stack
定义的编译时依赖性;触摸struct stack
时,必须重新编译。
注意,如果上面的struct stack
定义是堆栈的确切定义(堆栈的唯一属性是它有一个指向顶部节点的指针,它可以为null)那么,从物理上讲,一个{ {1}}指针实际上是指向指针的指针。我们可以使用struct stack *
名称来编写代码,以便我们可以使用任何定义:
typedef
无论哪种方式,/* Alternative "A" */
typedef struct node *stack_t; /* a stack_t type is a pointer to a node */
/* Alternative "B" */
typedef struct stack {
struct node *top;
} stack_t; /* stack_t is a structure containing a pointer to a node */
方面的API都是这样的:
stack_t
或其他什么。如果void stack_init(stack *s);
int stack_push(stack *s, int item);
是一个指针(上面的替代“A”),则stack
是一个指向指针的指针,因此你的代码将充满指向指针的操作。
如果你对指针到指针的语法都不熟悉,那么你可以给自己一个宏来假装它是一个结构。
stack *s
在代码中,您可以执行以下操作:
/* Alternative "A" */
typedef struct node *stack_t; /* a stack_t type is a pointer to a node */
#define stack_top(s) (*(s)) /* dereference stack s to obtain the top pointer */
/* Alternative "B" */
typedef struct stack {
struct node *top;
} stack_t; /* stack_t is a structure containing a pointer to a node */
#define stack_top(s) ((s)->top) /* dereference struct pointer to get top pointer */
如果您一直使用/* push new_node onto stack */
new_node->next = stack_top(s);
stack_top(s) = new_node;
访问器,现在可以在替代“A”和“B”之间翻转堆栈类型的表示,而无需重写任何代码(仅重新编译它)。
一些挑剔的C程序员会在stack_top
处畏缩,因为它看起来像是在分配一个函数调用(这在C中是不可能的,而不使用宏来弯曲语言),并且更喜欢“setter”函数stack_top(s) = new_node
。这大多只是过时的,狭隘的思考。
答案 2 :(得分:1)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int Item;
#define ItemFormat "%d"
struct node {
Item data;
struct node *next;
};
typedef struct node *Stack;
void push(Stack *st, Item x){
struct node *newnode = malloc(sizeof(struct node));
newnode->data = x;
newnode->next = *st;
*st = newnode;
}
bool isEmpty(Stack st){
return st == NULL;
}
Item pop(Stack *st) {
if(!isEmpty(*st)){
struct node *p = *st;
Item value = p->data;
*st = p->next;
free(p);
return value;
}
fprintf(stderr, "Stack is Empty!\n");
return (Item)0;
}
bool inputItem(Item *x){
int stat;
if(1==(stat=scanf(ItemFormat, x)))
return true;
if(stat == EOF)
return false;
scanf("%*[^\n]");
return false;
}
void printItem(Item x){
printf(ItemFormat, x);
}
int main(void){
Stack st = NULL, array[5] = { NULL };
Item x;
while(inputItem(&x)){
push(&array[1], x);
}
while(!isEmpty(array[1])){
x = pop(&array[1]);
printItem(x);
printf("\n");
}
/*
while(inputItem(&x)){
push(&st, x);
}
while(!isEmpty(st)){
x = pop(&st);
printItem(x);
printf("\n");
}
*/
return 0;
}
答案 3 :(得分:0)
C中单个数组中两个堆栈的静态实现看起来像这样...堆栈结构将具有两个top变量top1和top2。
struct stack
{
int data[MAX];
int top1,top2;
}s;
top1初始化为-1,而top2初始化为MAX
溢出条件: 1]
if((s->top1)+1==s->top2)
printf("Stack 1 overflow\n");
2]
if((s->top2)-1==s->top1)
printf("Stack 2 overflow\n");
下溢条件变得非常明显。由于我们可能会用完阵列中的存储空间,因此此方法可能不会提高内存效率,但这是单个阵列中多个堆栈的基本原理。