我想在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;
}
pop()
函数中的代码中存在内存泄漏。你应该释放你拥有malloc的内存。
从@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
而忽略了你。你可能想要更好地处理它。
您只能拥有一个堆栈,因为您将其定义为全局变量:
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)那么,从物理上讲,struct stack *
指针实际上是指向a的指针指针。我们可以使用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 */
无论哪种方式,根据stack_t
的API然后看起来像这样:
void stack_init(stack *s);
int stack_push(stack *s, int item);
管他呢。如果stack
是一个指针(上面的替代“A”),那么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;
如果您始终使用stack_top
访问器,则现在可以在替代“A”和“B”之间翻转堆栈类型的表示,而无需重写任何代码(仅重新编译它)。
一些挑剔的C程序员会在stack_top(s) = new_node
畏缩,因为它看起来像是一个函数调用(这在C中是不可能的,不使用宏来弯曲语言),并且更喜欢stack_top_set(s, new_node)
的“setter”函数。这大多只是过时的,狭隘的思维。
#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;
}
C中单个数组中两个堆栈的静态实现看起来像这样......堆栈结构将有两个顶部变量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");
下溢情况变得非常明显。此方法可能不具有内存效率,因为我们可能会耗尽阵列中的存储空间,但它是单个阵列中多个堆栈的基本基础。