I have made a few functions for some basic stack operations using linked lists. Please review my code and suggest some ways to increase its readability. I would also love to hear ideas on how to make my code more compact and simple.
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *next;
};
void Traversal(struct node *ptr)
{
do
{
printf("The element is : %d\n", ptr->data);
ptr = ptr->next;
} while (ptr->next != NULL);
}
int isEmpty(struct node *top)
{
if (top == NULL)
{
return 1;
}
else
{
return 0;
}
}
int isFull(struct node *top)
{
struct node *n = malloc(sizeof(struct node *));
if (n == NULL)
{
return 1;
}
else
{
return 0;
}
free(n);
}
struct node *push(struct node *top, int x)
{
if (isFull(top))
{
printf("Stack Overflow\n");
}
else
{
struct node *n = malloc(sizeof(struct node));
n->data = x;
n->next = top;
top = n;
}
return top;
}
struct node *pop(struct node *top)
{
if (isEmpty(top))
{
printf("Stack Underflow\n");
}
else
{
struct node *n = top;
top = top->next;
free(n);
}
return top;
}
struct node *peek(struct node *top, int atPosition) // To view the value stored at a specific postion
{
struct node *ptr = top;
if (atPosition < 0)
{
printf("Invalid Index\n");
}
else
{
for (int i = 1; i < atPosition && ptr->next != NULL; i++)
{
ptr = ptr->next;
}
printf("The data at index %d is %d\n", atPosition, ptr->data);
}
free(ptr);
return top;
}
struct node *stackTop(struct node *top)
{
printf("The data stored in stack top is %d\n", top->data);
return top;
}
struct node *stackBottom(struct node *top)
{
struct node *ptr = top;
while (ptr->next != NULL)
{
ptr = ptr->next;
}
printf("The data stored in stack bottom is %d\n", ptr->data);
free(ptr);
return top;
}
2 Answers 2
push()
has undefined behaviour any time its malloc()
returns a null pointer.
isFull()
leaks memory (because the matching free()
call is unreachable). Also, whether or not an allocation is successful tells us nothing about future allocations.
Instead of the broken and unreliable isFull()
, work on the principle of attempting the action and taking appropriate recovery steps when it fails (sometimes described as easier to ask for forgiveness than permission, or EAFP):
struct node *push(struct node *top, int x)
{
struct node *n = malloc(sizeof *n);
if (!n) { return n; } /* failure */
n->data = x;
n->next = top;
top = n;
return top;
}
It's usually a bad idea for low-level library functions to generate output (especially to stdout
) - you might want to use the code in a GUI program, and report errors via a status bar or pop-up window, for example. I would make it the caller's responsibility to examine the result and report it appropriately:
if (!push(top, x) {
fputs("Out of memory\n", stderr); /* Not "stack overflow" - that's misleading! */
}
-
\$\begingroup\$ Could you elaborate more on how to write a more reliable
isFull()
function applying the EAFP principle \$\endgroup\$Ultimate– Ultimate2022年05月21日 09:20:06 +00:00Commented May 21, 2022 at 9:20 -
1\$\begingroup\$ As I demonstrate,
isFull()
isn't necessary. Instead, where it's used (e.g. inpush()
, attempt the allocation, and take alternative action if it fails. There's no reliable way to know in advance whether allocation will succeed. "Container is full" is only meaningful for a container with a predetermined capacity. \$\endgroup\$Toby Speight– Toby Speight2022年05月21日 09:26:16 +00:00Commented May 21, 2022 at 9:26
isEmpty
is anti-idiomatic:if (top == NULL) { return 1; } else { return 0; }
is a very long way to say
return top == NULL;
isFull
is very dubious. Why callmalloc
twice?push
is very well capable to detect themalloc
failure.BTW, once
malloc
fails, it is unsafe to callprintf
.The utility library shall not print anything. Returning a success/failure indication is enough; let the caller take an appropriate recovery action.
In any case, error messages shall go to
stderr
, notstdout
.Why
peek
frees theptr
? Consider what happens onatPosition
being 1. You have stack corruption, and a memory leak right away.Also, if
atPosition
is larger than the stack size,peek
reports incorrect results (there is no such position, yet it prints something).stackBottom
should test that the stack is not empty. Otherwise,ptr->next
dereferences a null pointer.And again, it should not
free
anything.
-
\$\begingroup\$ In
peek
andstackBottom
, I usedfree()
because the main utility of these functions was only to print the required values to the user. Is there any specific reason why I should not usefree()
in these functions? \$\endgroup\$Ultimate– Ultimate2022年05月20日 17:22:09 +00:00Commented May 20, 2022 at 17:22 -
1\$\begingroup\$ Why do you say it's unsafe to call
printf()
after amalloc()
failure? That seems reasonable to me. \$\endgroup\$Toby Speight– Toby Speight2022年05月21日 07:38:19 +00:00Commented May 21, 2022 at 7:38