While doing a coding exercise in C, I had to allocate memory for a pointer to a structure (cur), even though the structure presumably already had memory pre-allocated for it, otherwise I would get an 'assignment to null pointer' type of error.
I was under the assumption that if the pointer was going to point to a structure with pre-allocated memory, allocating further memory would be reduntant? To clarify, the code compiles and runs without errors, just confused as to why I needed to allocate memory to achieve the intended behavior.
/* create a stack */
typedef struct {
int top;
struct TreeNode array[MAX_ARR_SIZE];
} Stack;
int node_comparator(const void *p, const void *q);
struct TreeNode *increasingBST(struct TreeNode *root) {
/* add all the nodes to an array via DFT */
int i, sorted_pos = 0;
struct TreeNode *start, *cur;
struct TreeNode sorted_nodes[MAX_ARR_SIZE];
Stack *node_stack = malloc(sizeof(Stack));
node_stack->top = -1;
node_stack->array[++node_stack->top] = *root;
/* below is the pointer in question
* originally, this line was not here */
cur = malloc(sizeof(struct TreeNode));
while (node_stack->top != -1) {
/* "pop" node off stack */
*cur = node_stack->array[node_stack->top--];
/* add node to array */
sorted_nodes[sorted_pos++] = *cur;
/* add right and left node to stack, if present */
if (cur->right != NULL)
node_stack->array[++node_stack->top] = *cur->right;
if (cur->left != NULL)
node_stack->array[++node_stack->top] = *cur->left;
}
/* etc... */
Here's a link to a gist for full context. Thanks!
The extra memory isn't redundant since you want to save the node's details off the stack. However, you don't need to use malloc. You can declare cur at the top of your function (struct TreeNode cur;) and then use memcpy (from string.h).
memcpy(&cur,&node_stack->array[node_stack->top--],sizeof(cur));
Since cur is no longer a pointer, you would need to edit your code and replace occurrences of cur-> with cur..
Related
I've created a Stack structure in C. When the stack is initialized with a value, I am able to print it back and receive the correct output. However, after pushing a new string, the print function prints what appears to be a random character (ASCII 177).
Originally, I implemented this project with pointers, but I was unable to get it working. Instead, I opted to only use a pointer for the Node *nodes member of Stack. That way, when I need to grow the stack, I can just multiply the amount of memory required by Stack.size. However, this approach has not worked yet either.
#define MAX_DATA 64
struct Node{
char val[MAX_DATA];
};
struct Stack{
int size;
struct Node *nodes;
};
These are used as follows:
struct Node node = {.val = "Test"};
struct Stack stack = newStack(node);
printStack(stack);
The newStack function initializes nodes properly. For the sake of inclusion:
struct Stack newStack(struct Node node)
{
struct Stack stack;
stack.size = 1;
stack.nodes = (struct Node*) malloc(sizeof(struct Node));
stack.nodes[0] = node;
return stack;
}
The stack is then printed iteratively in printStack(), with stack.size being the upper bound of the for-loop.
The trouble comes when I try to run:
struct Node node2 = {.val = "Test1"};
push(stack, node2);
printStack(stack);
The push function aims to create a temporary stack and assign the value of the stack to it. After this, the size is incremented, the pointer to the nodes is freed, and new memory is allocated, with room for a new member at the end.
void push(struct Stack stack, struct Node node)
{
struct Stack temp_stack = stack;
stack.size += 1;
free(stack.nodes);
stack.nodes = (struct Node*) malloc(sizeof(struct Node) * stack.size);
for(int i = 0; i < temp_stack.size; i++){
stack.nodes[i] = temp_stack.nodes[i];
}
stack.nodes[stack.size - 1] = node;
}
Needless to say, this doesn't execute properly.
The expected output would be:
Test
Test1
But, instead, I receive only ASCII-177. It is also worth noting that the execution hangs after it prints that and moves to the new line. This results in Aborted (core dumped).
Am I improperly freeing and re-allocating memory? Any help would be appreciated. Thank you in advance!
It's worth remembering that in C, passing a struct to a function is pass-by-value, i.e., the function gets a copy of the struct. All members of the struct, including pointer variables (but not whatever the pointer references), are duplicated.
So in the push function, think about what happens to the original when you modify this copy (e.g., stack.size += 1, and also when you free() the stack.nodes.
Thank you to peekay for the recommendations. I ended up doing a re-write. It became quite a bit simpler. I did remove a layer of "visibility" by storing the nodes as a pointer, but I suppose this implementation is a bit more true to the data structure.
Node is implemented as a means to hold data, and the user doesn't interact with it directly. It also points to the following Node. Stack is implemented to hold the top Node, and also the size of the Stack.
Node:
struct Node{
char val[MAX_DATA];
struct Node *next;
};
Stack:
struct Stack{
struct Node *top;
int size;
};
Push:
void push(struct Stack *stack, char *newVal)
{
struct Node *newNode;
newNode = (struct Node*) malloc(sizeof(struct Node));
strcpy(newNode->val, newVal);
newNode->next = stack->top;
stack->top = newNode;
stack->size++;
}
Usage:
struct Stack stack;
newStack(&stack);
push(&stack, "Test");
push(&stack, "Test1");
push(&stack, "Test2");
push(&stack, "Test3");
Complete Code
Updated, accessible Nodes
I'm getting started with dynamic lists and i don't understand why it is necessary to use the malloc function even when declaring the first node in the main() program, the piece of code below should just print the data contained in the first node but if i don't initialize the node with the malloc function it just doesn't work:
struct node{
int data;
struct node* next;
};
void insert(int val, struct node*);
int main() {
struct node* head ;
head->data = 2;
printf("%d \n", head->data);
}
You don’t technically, but maintaining all nodes with the same memory pattern is only an advantage to you, with no real disadvantages.
Just assume that all nodes are stored in the dynamic memory.
Your “insert” procedure would be better named something like “add” or (for full functional context) “cons”, and it should return the new node:
struct node* cons(int val, struct node* next)
{
struct node* this = (struct node*)malloc( sizeof struct node );
if (!this) return next; // or some other error condition!
this->data = val;
this->next = next;
return this;
}
Building lists is now very easy:
int main()
{
struct node* xs = cons( 2, cons( 3, cons( 5, cons( 7, NULL ) ) ) );
// You now have a list of the first four prime numbers.
And it is easy to handle them.
// Let’s print them!
{
struct node* p = xs;
while (p)
{
printf( "%d ", p->data );
p = p->next;
}
printf( "\n" );
}
// Let’s get the length!
int length = 0;
{
struct node* p = xs;
while (p)
{
length += 1;
p = p->next;
}
}
printf( "xs is %d elements long.\n", length );
By the way, you should try to be as consistent as possible when naming things. You have named the node data “data” but the constructor’s argument calls it “val”. You should pick one and stick to it.
Also, it is common to:
typedef struct node node;
Now in every place except inside the definition of struct node you can just use the word node.
Oh, and I almost forgot: Don’t forget to clean up with a proper destructor.
node* destroy( node* root )
{
if (!root) return NULL;
destroy( root->next );
free( root );
return NULL;
}
And an addendum to main():
int main()
{
node* xs = ...
...
xs = destroy( xs );
}
When you declare a variable, you define the type of the variable, then it's
name and optionally you declare it's initial value.
Every type needs an specific amount of memory. For example int would be
32 bit long on a 32bit OS, 8 bit long on a 64.
A variable declared in a function is usually stored in the stack associated
with the function. When the function returns, the stack for that function is
no longer available and the variable does not longer exist.
When you need the value/object of the variable to exist even after a function
returns, then you need to allocate memory on a different part of the program,
usually the heap. That's exactly what malloc, realloc and calloc do.
Doing
struct node* head ;
head->data = 2;
is just wrong. You've declaring a pointer named head of type struct node,
but you are not assigning anything to it. So it points to an unspecified
location in memory. head->data = 2 tries to store a value at an unspecified
location and the program will most likely crash with a segfault.
In main you could do this:
int main(void)
{
struct node head;
head.data = 2;
printf("%d \n", head.data);
return 0;
}
head will be saved in the stack and will persist as long as main doesn't
return. But this is only a very small example. In a complex program where you
have many more variables, objects, etc. it's a bad idea to simply declare all
variables you need in main. So it's best that objects get created when they
are needed.
For example you could have a function that creates the object and another one
that calls create_node and uses that object.
struct node *create_node(int data)
{
struct node *head = malloc(sizeof *head);
if(head == NULL)
return NULL; // no more memory left
head->data = data;
head->next = NULL;
return head;
}
struct node *foo(void)
{
struct node *head = create_node(112);
// do somethig with head
return head;
}
Here create_node uses malloc to allocate memory for one struct node
object, initializes the object with some values and returns a pointer to that memory location.
foo calls create_node and does something with it and it returns the
object. If another function calls foo, this function will get the object.
There are also other reasons for malloc. Consider this code:
void foo(void)
{
int numbers[4] = { 1, 3, 5, 7 };
...
}
In this case you know that you will need 4 integers. But sometimes you need an
array where the number of elements is only known during runtime, for example
because it depends on some user input. For this you can also use malloc.
void foo(int size)
{
int *numbers = malloc(size * sizeof *numbers);
// now you have "size" elements
...
free(numbers); // freeing memory
}
When you use malloc, realloc, calloc, you'll need to free the memory. If
your program does not need the memory anymore, you have to use free (like in
the last example. Note that for simplicity I omitted the use of free in the
examples with struct head.
What you have invokes undefined behavior because you don't really have a node,, you have a pointer to a node that doesn't actually point to a node. Using malloc and friends creates a memory region where an actual node object can reside, and where a node pointer can point to.
In your code, struct node* head is a pointer that points to nowhere, and dereferencing it as you have done is undefined behavior (which can commonly cause a segfault). You must point head to a valid struct node before you can safely dereference it. One way is like this:
int main() {
struct node* head;
struct node myNode;
head = &myNode; // assigning the address of myNode to head, now head points somewhere
head->data = 2; // this is legal
printf("%d \n", head->data); // will print 2
}
But in the above example, myNode is a local variable, and will go out of scope as soon as the function exists (in this case main). As you say in your question, for linked lists you generally want to malloc the data so it can be used outside of the current scope.
int main() {
struct node* head = malloc(sizeof struct node);
if (head != NULL)
{
// we received a valid memory block, so we can safely dereference
// you should ALWAYS initialize/assign memory when you allocate it.
// malloc does not do this, but calloc does (initializes it to 0) if you want to use that
// you can use malloc and memset together.. in this case there's just
// two fields, so we can initialize via assignment.
head->data = 2;
head->next = NULL;
printf("%d \n", head->data);
// clean up memory when we're done using it
free(head);
}
else
{
// we were unable to obtain memory
fprintf(stderr, "Unable to allocate memory!\n");
}
return 0;
}
This is a very simple example. Normally for a linked list, you'll have insert function(s) (where the mallocing generally takes place and remove function(s) (where the freeing generally takes place. You'll at least have a head pointer that always points to the first item in the list, and for a double-linked list you'll want a tail pointer as well. There can also be print functions, deleteEntireList functions, etc. But one way or another, you must allocate space for an actual object. malloc is a way to do that so the validity of the memory persists throughout runtime of your program.
edit:
Incorrect. This absolutely applies to int and int*,, it applies to any object and pointer(s) to it. If you were to have the following:
int main() {
int* head;
*head = 2; // head uninitialized and unassigned, this is UB
printf("%d\n", *head); // UB again
return 0;
}
this is every bit of undefined behavior as you have in your OP. A pointer must point to something valid before you can dereference it. In the above code, head is uninitialized, it doesn't point to anything deterministically, and as soon as you do *head (whether to read or write), you're invoking undefined behavior. Just as with your struct node, you must do something like following to be correct:
int main() {
int myInt; // creates space for an actual int in automatic storage (most likely the stack)
int* head = &myInt; // now head points to a valid memory location, namely myInt
*head = 2; // now myInt == 2
printf("%d\n", *head); // prints 2
return 0;
}
or you can do
int main() {
int* head = malloc(sizeof int); // silly to malloc a single int, but this is for illustration purposes
if (head != NULL)
{
// space for an int was returned to us from the heap
*head = 2; // now the unnamed int that head points to is 2
printf("%d\n", *head); // prints out 2
// don't forget to clean up
free(head);
}
else
{
// handle error, print error message, etc
}
return 0;
}
These rules are true for any primitive type or data structure you're dealing with. Pointers must point to something, otherwise dereferencing them is undefined behavior, and you hope you get a segfault when that happens so you can track down the errors before your TA grades it or before the customer demo. Murphy's law dictates UB will always crash your code when it's being presented.
Statement struct node* head; defines a pointer to a node object, but not the node object itself. As you do not initialize the pointer (i.e. by letting it point to a node object created by, for example, a malloc-statement), dereferencing this pointer as you do with head->data yields undefined behaviour.
Two ways to overcome this, (1) either allocate memory dynamically - yielding an object with dynamic storage duration, or (2) define the object itself as an, for example, local variable with automatic storage duration:
(1) dynamic storage duration
int main() {
struct node* head = calloc(1, sizeof(struct node));
if (head) {
head->data = 2;
printf("%d \n", head->data);
free(head);
}
}
(2) automatic storage duration
int main() {
struct node head;
head.data = 2;
printf("%d \n", head.data);
}
I am trying to understand malloc() better when it comes to linked list. Does this create memory for the pointer to the list as well as the fields inside of it?
Such as:
SomeStruct * someStructPtr = (SomeStruct *) malloc(sizeof(SomeStruct));
Alongside this, if I am trying to free this node AND the fields inside of it. Do I need to walk through all the fields (see code below). Does this just free the pointer (created from the line above) or does it free the pointer and and the fields.
free(someStructPtr);
For a more direct example. I have a struct of List and I'm trying to allocate space for this struct AND it's fields when it is created. I want to make sure I am creating, deleting, and using stdrup() correctly. My code is below:
Struct Declaration:
typedef struct ListNode_st
{
char * str;
struct ListNode_st * next;
} List;
Create Node:
List * List_createNode(const char * str){
List * listPointer = (List *) malloc(sizeof(List));
//make sure there was enough memory
if(listPointer == NULL)
return NULL;
//malloc for next
listPointer.next = (List *) malloc(sizeof(List *));
if(listPointer.next == NULL)
return NULL;
listPointer.next = NULL;
//malloc for str
listPointer.str = strdup(str);
//make sure enough space for the string to be copied
if(listPointer.str == NULL)
return NULL;
//return value
return listPointer;
}
Delete Node:
void List_destory(List * list){
//base case -- (last item)
if(list == NULL)
return;
//recurse case -- (there are more items)
List_destroy(list->next);
if(list->str != NULL)
free(list->str);
if(list->next != NULL)
free(list->next);
free(list);
}
Please don't cast the return value from malloc().
malloc(sizeof(SomeStruct))
allocates enough memory for one struct, and you then have to initialise every field within the struct.
calloc(1, sizeof(SomeStruct))
does the same but every byte of the struct is initialised to 0. malloc() and calloc() know nothing about your struct, they simply allocate the amount of memory you asked for. They return a void* pointer so that they can be used with any data type, about which they neither know nor care.
When you come to free() the allocated memory, the same applies - free() knows nothing about your struct, or even if it is a struct. It's up to you to free() any memory allocation within that struct, before you free() the memory for the struct. It does not free a struct. It frees the memory where the struct is.
So the answer is yes, you do need to walk through the whole data structure, otherwise any nested memory allocation pointers will be lost, resulting in memory leak.
List * List_createNode(const char * str){
List * listPointer = (List *) malloc(sizeof(List));
if(listPointer == NULL)
return NULL;
listPointer->next = NULL;
listPointer->str = strdup(str);
if(listPointer->str == NULL){
free(listPointer);
return NULL;
}
return listPointer;
}
void List_destory(List * list){
if(list == NULL)
return;
List_destroy(list->next);
free(list->str);
free(list);
}
Let's warm up a little before we tackle your question.
int x = 42;
Where does the memory of x come from? Is it allocated? Where? If this line is inside a function, the answer is: it is an "automatic" variable and the memory it uses is on the stack.
int * y = NULL;
Where does the memory for y come from now? There was no call to malloc() here and yet, y exists.
By now, you should be able to answer the first part of your question. malloc() is not allocating the memory for your pointer in this case.
struct Node
{
struct Node * next;
int value;
};
void InitNode( Node *node, int value )
{
if(NULL != node)
{
node->next = NULL;
node->value = value;
}
}
// Not on HEAP, not on stack! Where?
// This one gets its memory from "initvars" section.
int foo = 69;
int main( int argc, const char * argv[] )
{
struct Node n1; // instance located on stack...
InitNode(&n1); // no malloc() to be seen...
// Instance located on HEAP, but n2, the pointer is located on stack,
// just like an int x = 42 would be.
struct Node *n2 = malloc(sizeof(struct Node));
}
So, obviously in order to de-mystify what malloc() does requires to understand the different types of memory and how to deal with it.
So far we saw "automatic" aka "stack memory", we saw "initvars" memory. And we saw "heap" memory. malloc() is the function you use to allocate memory on the heap.
Last not least, there are yet other types of memory I will not mention here.
Does this create memory for the pointer to the list as well as the fields inside of it?
Given your struct type
typedef struct ListNode_st
{
char * str;
struct ListNode_st * next;
} List;
the following call will create memory for an instance of that struct:
List *l = malloc( sizeof *l ); // note no cast, operand of sizeof
l points to a block of memory large enough to contain two pointers; however, nothing's been allocated for either str or next to point to. You would have to allocate memory for the string and the next node separately:
l->str = malloc( N * sizeof *l->str ); // allocate N characters
or
l->str = strdup( str );
Note that in most linked list implementations, you don't allocate memory for next when you create a new node; that field is meant to point to another, previously allocated node in the list. You will set it when you insert a node following the current node.
Alongside this, if I am trying to free this node AND the fields inside of it. Do I need to walk through all the fields (see code below). Does this just free the pointer (created from the line above) or does it free the pointer and and the fields.
You need to free any allocated members first before deleting the entire node, such as
free( l->str );
free( l );
Freeing l does not free the memory that l->str points to.
So I'm doing some linked list revison and Im trying to just load a list with some numbers and then print it out. Below is my code:
#include <stdio.h>
#include <stdlib.h>
typedef struct stack {
int data;
struct stack *next;
}*stack;
stack create_s(void){
stack s = (void*)malloc(sizeof(stack));
s->next = NULL;
return s;
}
void push_s(stack s, int data) {
while (s->next != NULL) {
s = s->next;
}
s->next = (void*)malloc(sizeof(stack));
s=s->next;
s->data = data;
s->next = NULL;
}
void print_s(stack s) {
if (s==NULL) {
return;
}
else {
while (s->next != NULL) {
printf("%d\n",s->data);
s=s->next;
}
}
}
int main (void) {
stack s = create_s();
push_s(s,2);
push_s(s,4);
push_s(s,6);
push_s(s,8);
print_s(s);
return 0;
}
My output is however:
-1853045587
2
4
6
when it should be
2
4
6
8
Is it printing the address of my struct at the beginning? Also, why is it not printing my last element?
Thanks
The code contains several errors, but the first thing that catches the eye is that your memory allocation is already obviously broken
stack s = (void*)malloc(sizeof(stack));
You defined stack as a pointer type. This means that sizeof(stack) evaluates to pointer size and the above malloc allocates enough space to store a single pointer, not enough for the entire struct stack object. The same memory allocation error is present in push_s as well.
Here's some advice
Don't hide pointer types behind typedef names. Define your stack as
typedef struct stack{
int data;
struct stack *next;
} stack;
and use stack * wherever you need a pointer. I.e. make that * visible instead of hiding it "inside" a typedef name. This will make your code easier to read.
Don't cast the result of malloc. Anyway, what is the point of casting it to void * when it is void * already???
Don't use sizeof with types unless you really really have to. Prefer to use sizeof with expressions. Learn to use the following malloc idiom
T *p = malloc(sizeof *p);
or, in your case
struct stack *s = malloc(sizeof *s);
This will allocate a memory block of appropriate size.
Also, as #WhozCraig noted in the comments, the very first node in your list is apparently supposed to serve as a "sentinel" head node (with undefined data value). In your code you never initialize the data value in that head node. Yet in your print_s function you attempt to print data value from the head node. No wonder you get garbage (-1853045587) as the first line in your output. Don't print the very first node. Skip it, if it really is supposed to serve as a sentinel.
Also, the cycle termination condition in print_s looks strange
while (s->next != NULL)
Why are you checking s->next for NULL instead of checking s itself? This condition will terminate the cycle prematurely, without attempting to print the very last node in the list. This is the reason why you don't see the last element (8) in your output.
The actual cause of the given output can be fixed by changing:
s=s->next;
s->data = data;
to
s->data = data;
s=s->next;
I wanted to make an array of stacks in C, where I should be able to retain individual stacks and their respective information. I currently have the following implementation, which only works for one stack. How can I modify the functions push and pop to achieve multiple stacks each using the same function.
(I was easily able to do this in Java, as I could create a class, but I have no idea in 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;
}
You have a memory leak in your code in the pop() function. You should free the memory that you have malloc'd.
Taking advice from #Jongware's comments below your question.
Here's a new version of the push() and pop() functions.
#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;
}
And here's an example of how to use them:
#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;
}
As for where you should be declaring your stack pointers that is really up to you and how you intend to use them.
Have a read about C variable scope for some options and how you might use them.
Also, I will have to include a warning with declaring these pointers inside functions. In whichever function you declare your pointer you must make sure that you pop everything off the stack before you exit the function, otherwise you will lose the pointer and leak all the allocated memory. If this is not what you want, or you want the pointer to outlive the function then you can declare the pointer globally or pass it around, making sure that everything is popped off the stack before your program exists or loses the pointer.
Another thing that you might want to consider is what happens when you use pop() on an empty stack? The implementation that I have given you simply returns 0 and ignores you. You might want to handle that a little better.
You can only have one stack because you defined it as a global variable:
struct node *first = NULL;
In Java you would have used a class. In C, you can likewise do "object based" programming by defining an abstract data type which holds your instance variables, instead of using global variables:
struct stack {
struct node *first;
};
there are no class features like constructors or destructors, so you write functions to initialize a stack, destroy a stack and so forth. To achieve multiple instantiation, you explicitly pass a stack * argument to each function in the stack module.
You might want to name your functions in some consistent way, like stack_init, stack_cleanup, stack_push and so on.
There are design questions to settle such as: does the caller allocate struct stack, for which you provide stack_init function? Or do you provide a one-step stack_alloc function that allocates and returns a stack? Or perhaps both, so the user can choose performance or convenience?
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 */
It's possible to do information hiding in C, whereby you can completely conceal from the client code (which uses the stack module) what a struct stack is.
However, if you provide a stack_init, then the client has to know how large a stack is, since it provides the memory for it. Generally, modules which completely hide an implementation also hide how large it is, and so provide only a stack_alloc and stack_free type interface.
An advantage of that is that client code doesn't have to be recompiled if the stack module is changed and the structure is larger. This is very good if you're writing a widely-used library: it is easy for users to upgrade or possibly downgrade.
However, revealing the implementation allows for more efficient code, since the client has the freedom to choose memory management for stacks. Stacks can be declared as local variables in automatic storage ("on the stack", so to speak), statically as global variables, or packed into arrays.
The user can do things like:
{
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 */
}
and things like:
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 */
All this code has a compile-time dependency of the definition of struct stack; whenever struct stack is touched, it must be recompiled.
Note that if the above struct stack definition is the exact definition for a stack (the only property of a stack is that it has a pointer to a top node which can be null) then, physically speaking, a struct stack * pointer is actually a pointer to a pointer. We can use a typedef name to write the code so that we can use either definition:
/* 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 */
Either way, the API in terms of stack_t then looks like this:
void stack_init(stack *s);
int stack_push(stack *s, int item);
or whatever. If stack is a pointer (alternative "A" above) then stack *s is a pointer-to-pointer, and so your code will be full of pointer-to-pointer manipulation.
If you're not comfortable with pointer-to-pointer syntax all over the place, then you can give yourself a macro to pretend that it's a structure anyway.
/* 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 */
In the code you can then do things like:
/* push new_node onto stack */
new_node->next = stack_top(s);
stack_top(s) = new_node;
If you consistently use the stack_top accessor, you can now flip the representation of the stack type between alternative "A" and "B" without rewriting any of your code (only recompiling it).
Some nit-picky C programmers will cringe at stack_top(s) = new_node since it looks like a function call is being assigned (which is impossible in C without using macros to bend the language), and prefer a "setter" function for that stack_top_set(s, new_node). That's mostly just outdated, parochial thinking.
#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;
}
The static implemetation of two stacks in a single array in C looks something like this...the stack structure will have two top variables top1 and top2.
struct stack
{
int data[MAX];
int top1,top2;
}s;
top1 is initialized to -1 while top2 is initialized to MAX
Overflow condtitions:
1]
if((s->top1)+1==s->top2)
printf("Stack 1 overflow\n");
2]
if((s->top2)-1==s->top1)
printf("Stack 2 overflow\n");
The underflow conditions become pretty obvious. This method may not be memory efficient since we might run out of storage space in the array but it is the basic fundamentals of multiple stacks in a single array.