I am asked to implement a stack using a linked list. The program declares two "stacks" and uses them in order to sort some integers stored inside a file. I have the following program that crashed at the beginning. I think the it crashes in the first while of the else. (for the file, the user enters its name)
/************************************************************************************************************************
Input File: A text file named: "Numbers.txt" that contains integers in random order. These integers are seperated
by either a space or a new line. This file is very essencial for the program to work, because from it the
program gets the integers. below is an example of the file format: (Note that there is a new line after
the last integer in order for the file to be read correctly).
2 1 3 5 4
7 6 8 9
Output File: None
Description: This program reads integers in random order. These integers are sorted by the end of the program's execution.
The sorting operations, using Push and Pop functions are managed by two structures of type stack_t.
The first structure is the main one that will be shown to the user, the second one is the one that holds the
unsorted integers while sorting. By the end stack2 will be empty and stack 1 will be sorted.
*************************************************************************************************************************/
#include <stdio.h>
#define MAXSTACK 10 // Defining the size of the stack as being 10 elements
typedef struct node s_Node;
struct node {
int element;
s_Node*next;
}; // Define the node_t structure with the element of the node and the pointer to the next node
typedef struct{
s_Node *top_stack;
}stack_t; // Define the stack_t structure with a pointer to s_Node (the first element of the linked list)
/*-------------------------- NEEDED PROTOTYPES --------------------------*/
// Initializes a new stack and returns it
stack_t NewStack();
// Returns 1 if the stack is empty, 0 otherwise
int Empty(stack_t q);
// Returns 1 if the stack is full, 0 otherwise
int Full(stack_t q);
// Pushes the element e in the stack. Returns 1 if it could Push.
int Push(int e, stack_t *q);
// Pops an element, placing it in e. Returns 1 if it could Pop.
int Pop(int *e, stack_t *q);
// Search for the top element in the stack and returns it.
int Peek (stack_t s);
// Displays the content of the stack.
void Printstack(const stack_t );
/*============================ MAIN PROGRAM ============================*/
main () {
char filename [20];
int newint, val1, val2;
FILE *file;
stack_t stack1;
stack_t stack2;
stack1=NewStack(); //Initializing the stack
stack2=NewStack();
printf ("\n\tPlease Enter the name of the file that contains your integers: ");
scanf ("%s", &filename);
file = fopen (filename, "r");
if (file!=NULL) {
fscanf (file, "%d", &newint);
while (!feof (file)&&!Full (stack1))
{
if ( Empty (stack1)) // Putting the first integer read from the file into the first stack.
{
Push (newint, &stack1);
}
else if (newint <= Peek(stack1)) //If the integer read from the file is less or equal to the integer in the top of the stack
{
Push(newint, &stack1); // Push the integer into stack1.
}
else
{
while ( newint > Peek (stack1) )
{ //Repeat poping from stack1 and pushing into stack2
// until it the integer read from the file is bigger than the top element of stack1
printf ("gOT into THE WHILE LOOP\n");
Pop (&val1,&stack1);
Push (val1, &stack2);
Printstack (stack2);
}
printf ("gOT OUT OF THE WHILE LOOP\n");
Push (newint, &stack1); //Pushing the integer read from the file into the first stack.
Printstack (stack1);
while (!Empty(stack2))
{ // Keep poping elements from stack2 and pushing them into stack1 until stack2 is empty
Pop (&val2,&stack2);
Push (val2, &stack1);
}
}
fscanf (file, "%d", &newint);
} // End while
if (feof(file)) printf ("\n\tYou have reached the end of the file: %s.\n\tThere are no more elements to read.",filename);
else printf ("\n\n\tYour stack is full.\n\tYou can not enstack more elemeents");
fclose (file);
printf ("\nThe final result");
Printstack (stack1);
}else printf ("Error!!! Your file did not open successefully!\n"); // Error checking in reading the file
} // End main
/*-------------------------- FUNCTION DEFINITIONS --------------------------*/
// Initializes a new stack and returns it
stack_t NewStack()
{
stack_t *n_stack;
n_stack=(stack_t*)malloc(sizeof(stack_t));
n_stack->top_stack=NULL;
return (*n_stack);
}
// Returns 1 if the stack is empty, 0 otherwise
int Empty(stack_t q)
{
if(q.top_stack==NULL)
return 1;
else
return 0;
}
// Returns 1 if the stack is full, 0 otherwise
int Full(stack_t q)
{
return 0;
}
// Pushes the element e in the stack. Returns 1 if it could Push.
int Push(int e, stack_t *q)
{
s_Node *nn;
nn= (s_Node*)malloc(sizeof(s_Node));
if(Full(*q))
{
printf("\n\t Stack is Full !! \n\n");
return 0; // return 0 if enstack NOT successful
}
else
{
nn->element=e;
nn->next=q->top_stack;
q->top_stack=nn;
return 1;
}
}
// Pops an element, placing it in e. Returns 1 if it could Pop.
int Pop(int *e, stack_t *q)
{
s_Node *temp;
if(Empty(*q)) // Check if the stack is empty
{
return 0; // popping Failed
}
else
{
temp=q->top_stack; // store the top in a temp ptr Node to free it after
*e=q->top_stack->element; // Store the popped element in e
q->top_stack=q->top_stack->next; // Point the top to the next element
free(temp); // Free the popped Node
return 1;
}
}
void Printstack(const stack_t q)
{
if(Empty(q))
printf("\n\tStack is empty!!\n");
else{
s_Node *walker;
walker = q.top_stack;
printf("\n\t This is the content of the stack:\n");
while(walker!=NULL)
{
printf("\t%d ",walker->element);
walker=walker->next;
}
printf("\n");
}
}
int Peek (stack_t s)
{ printf ("%d\n",s.top_stack->element);
return s.top_stack->element; // Returning the top element of the stack
}
Thank you in advance.
You're deferencing a NULL here:
int Peek (stack_t s)
{ printf ("%d\n",s.top_stack->element);
During this sequence:
else
{
while ( newint > Peek (stack1) )
{ //Repeat poping from stack1 and pushing into stack2
// until it the integer read from the file is bigger than the top element of stack1
printf ("gOT into THE WHILE LOOP\n");
Pop (&val1,&stack1);
Push (val1, &stack2);
Printstack (stack2);
}
Because of your input list:
2 1 3
3 is larger than everything on your stack so you pop everything off while looking for an element larger than 3, that leaves your stack1.top_stack as NULL. Your check should be:
while (!Empty(stack1) && (newint > Peek (stack1)))
Change
while ( newint > Peek (stack1) )
to
while ( !Empty(stack1) && newint > Peek (stack1) )
Since your peek function doesn't handle Empty stacks, you have to check for it explicitly.
Related
I'm trying to implement a stack that is read from a data file, without using arrays. I have all the file codes, I'm just modifying the main tester code.
This was given as a step.. but I'm not sure what he's asking, besides removing all of the array codes:
"4. Edit the tester code to read in the data from the file you create. You must remove all
code dealing with an array and update the code to read in the data and place it directly
in the stack using only stack functions. You cannot hardcode the number of lines you
read in from the file nor can you change any code in the stack. How you do this is the
challenge. "
The text file (StackData.txt) will have the following data (both the number and taskname):
1 task1
2 task2A
7 task3A
9 task2B
3 task4A
2 task4B
6 task3B
This is what I have so far:
// printf support
#include <stdio.h>
// stack callable routines
#include "Stack.h"
// UserData definition for making and getting stack data
#include "UserData.h"
// local functions
// PrintStackItem is a local function that we can call to print out a message (msg) and
// a UserData item. So we can see how many things are allocated as we proceed,
// it will also print out the number of things allocated
static void PrintStackItem (char msg[], UserData D);
// PrintAllocations is a local function that will print out a message (msg) and the
// current global AllocationCount
static void PrintAllocations (char msg[]);
int main(int argc, const char * argv[]) {
FILE* in = fopen("StackData.txt", "r");
int fscanf(FILE *in, char txt[50]);
if (in == NULL) {
printf("File not found.");
return 1;
}
// Show the allocation count when we start
PrintAllocations ("On startup");
// create a stack ans see the effect of on the number of allocations
Stack S = initStack();
PrintAllocations ("After initStack called");
// push the data on the stack, showing the data and allocations
for (int loop = 0; loop < in; loop++)
{
UserData D = FILE *in[loop];
push (S, D);
PrintStackItem("push called, data is", D);
}
// pop and print the stack content
// peek at the data before popping it so we can see what peek yields
while (!empty(S))
{
PrintStackItem ("peek called, data is", peek(S));
PrintStackItem ("pop called, data is", pop(S));
}
// delete the stack and see the effect on the allocations
PrintAllocations ("Before deleteStack called");
S = deleteStack(S);
PrintAllocations ("After deleteStack called");
return 0;
}
/*
PrintStackData prints out the received message and the data in UserData
*/
void PrintStackItem (char msg[], UserData D)
{
printf ("%s %d, #allocations is %d\n", msg, D.num, AllocationCount);
}
/*
PrintAllocations prints out the received message and the current allocation count
The allocation count is global AllocationCount
*/
void PrintAllocations (char msg[])
{
printf ("%s, #allocations is %d\n", msg, AllocationCount);
return;
}
This is the code for Stack:
#ifndef Stack_h
#define Stack_h
// The calls on a stack need to pass or return UserData
#include "UserData.h"
// Our stack will use a linked list, so we need to resolve LLInfoPtr
#include "LinkedList.h"
// The stack empty() call returns a boolean
#include <stdbool.h>
// This is the layout of a stack. Notice that it contains
// a pointer to our underlying linked list and a simple boolean
// to indicate if our stack is empty (true) or not empty (false)
typedef struct {
LLInfoPtr LL;
bool empty;
} StackInfo, *Stack;
// initStack() allocates a stack and initializes it
Stack initStack();
// empty() returns the boolean for the Stack S (true is empty, false
is not empty)
bool empty(Stack S);
// push() places the UserData on the top of the stack
void push (Stack S, UserData D);
// pop() returns the UserData on the top of the stack and deletes
// the data from the stack
UserData pop (Stack S);
// peep() returns the UserData on the top of the stack but will not
// delete it from the stack
UserData peek (Stack S);
// deleteStack() deletes the frees the storage that was allocated by
the call
// to initStack()
Stack deleteStack(Stack S);
#endif /* Stack_h */
This is the code for UserData:
#ifndef USERDATA_H_INCLUDED
#define USERDATA_H_INCLUDED
// The UserData struct defines what each node in the LL
// contains.
// User data in each node contains an integer
typedef struct {
int num;
} UserData, *UserDataPtr;
#endif // USERDATA_H_INCLUDED
This is the code for Linkedlist:
#ifndef LINKEDLIST_H_INCLUDED
#define LINKEDLIST_H_INCLUDED
// The LL functions use UserData
#include "UserData.h"
// The Linked List needs the definition of what a Node is. A Node has
// UserData and linkage information for both "next and "prev"
// for a doubly linked list).
typedef struct node
{
UserData Data;
struct node *next;
struct node *prev;
} Node, *NodePtr;
// A LL Information block contains Head and Tail pointers to a LL
// For speed, it also contains a running count of the number of nodes
// currently in the LL started at Head and finishing at Tail.
// Head is used when adding or removing from the LL front,
// Tail is needed only when adding to the end of the LL
typedef struct {
NodePtr Head;
NodePtr Tail;
int NumNodesInList;
} LLInfo, *LLInfoPtr;
// Verifying allocation / deallocation of dynamic memory is done through
// AllocationCount. The variable is declared in LinkedList.c and is linked to
// through the extern
extern int AllocationCount;
// ShouldDelete is an enum that has two valid values called DELETE_NODE
// and RETAIN_NODE that are used in calling to get user data from the front
// of the LL
typedef int ShouldDelete;
enum ShouldDelete {DELETE_NODE=1, RETAIN_NODE=2};
// declarations for LL callable functions follow
// LL_Init allocates a LL Information structure, initializing Head, Tail and NumNodesInList
// and returning the address of the structure
LLInfoPtr LL_Init ();
// LL_Delete frees up the LL Information structure
LLInfoPtr LL_Delete (LLInfoPtr LLI_Ptr);
// LL_AddAtFront adds user data to the front of the underlying LL accessed through
// the LL Information struct
void LL_AddAtFront (LLInfoPtr LLI_Ptr, UserData theData);
// LL_AddAtEnd adds user data to the Tail of the underlying LL accessed through the
// LL information struct
void LL_AddAtEnd (LLInfoPtr LLI_Ptr, UserData theData);
// LL_GetFront returns the user data currently at the Head of the underlying LL and
//, optionally removes the user data from the LL
UserData LL_GetFront (LLInfoPtr LLI_Ptr, ShouldDelete Choice);
// LL_Length returns the number of nodes in the underlying LL
int LL_Length (LLInfoPtr LLI_Ptr);
// LL_GetAtIndex returns the node at the specified index starting at 0
UserData LL_GetAtIndex (LLInfoPtr, int FetchIndex);
// LL_SetAtIndex updates the node at the specified index starting at 0
void LL_SetAtIndex (LLInfoPtr LLI_Ptr, UserData D, int UpdateIndex);
// LL_Swap swaps the nodes in the underlying LL specified by indices starting at 0
void LL_Swap (LLInfoPtr LLI_Ptr, int Index1, int Index2);
#endif // LINKEDLIST_H_INCLUDED
I didn't get an output..but here are the errors:
Error messages
The original snippet of code that you shared was missing definitions of UserData and Stack, all the functions (initStack(), deleteStack(), push(), peek(), pop(), empty()) so this is probably the best we can do to help you:
Delete the declaration "int fscanf(FILE *in, char txt[50]);". You get that correct one from stdio.h already.
The loop:
for (int loop = 0; loop < in; loop++)
{
UserData D = FILE *in[loop];
push (S, D);
PrintStackItem("push called, data is", D);
}
should probably be something like:
for(;;) {
UserData D;
int rv = fscanf(in, "%d%*s", &D.num); // %*s will discard the string as you you don't seem to have a place to store it in your UserData.
if(rv != 1)
break;
push (S, D);
PrintStackItem("push called, data is", D);
}
Delete the PrintStackItem() and PrintAllocations() declarations and instead moved main() the bottom of your file.
Here's the minimal required for your original code to compile:
#include <stdio.h>
int AllocationCount = 0;
typedef struct {
} Stack;
typedef struct {
int num;
} UserData;
Stack initStack() {
return (Stack) {};
}
Stack deleteStack(Stack S) {
return (Stack) {};
}
void push(Stack S, UserData D) {
}
UserData peek(Stack S) {
return (UserData) { 0 };
}
UserData pop(Stack S) {
return (UserData) { 0 };
}
int empty(Stack S) {
return 1;
}
void PrintStackItem (char msg[], UserData D) {
printf ("%s %d, #allocations is %d\n", msg, D.num, AllocationCount);
}
void PrintAllocations (char msg[]) {
printf ("%s, #allocations is %d\n", msg, AllocationCount);
return;
}
int main(int argc, const char * argv[]) {
FILE* in = fopen("StackData.txt", "r");
if (!in) {
printf("File not found.");
return 1;
}
PrintAllocations ("On startup");
Stack S = initStack();
PrintAllocations ("After initStack called");
for (;;) {
UserData D;
int rv = fscanf(in, "%d%*s", &D.num);
if(rv != 2)
break;
push (S, D);
PrintStackItem("push called, data is", D);
}
while (!empty(S)) {
PrintStackItem ("peek called, data is", peek(S));
PrintStackItem ("pop called, data is", pop(S));
}
PrintAllocations ("Before deleteStack called");
deleteStack(S);
PrintAllocations ("After deleteStack called");
}
and here is resulting output:
On startup, #allocations is 0
After initStack called, #allocations is 0
push called, data is 1, #allocations is 0
push called, data is 2, #allocations is 0
push called, data is 7, #allocations is 0
push called, data is 9, #allocations is 0
push called, data is 3, #allocations is 0
push called, data is 2, #allocations is 0
push called, data is 6, #allocations is 0
Before deleteStack called, #allocations is 0
After deleteStack called, #allocations is 0
Once you remove those dummy functions it should work more or less.
Numbers are pushed onto the stack, you need to pop the full stack, after which, the stack in which elements less than 10 are removed. Example code when all elements of the stack are removed:
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <stdio.h>
#include <math.h>
#include <malloc.h>
struct kkk
{
float elem[15];
int top; // index of the top rlrment
};
struct kkk* st, * element; // pointers
void Init(struct kkk* st) // initialization
{
st->top = NULL;
}
void Push(struct kkk* st, float f) // push an item onto the stack
{
if (st->top < 15)
{
st->elem[st->top] = f;
st->top++;
}
else
printf("Stack full\n");
}
float Pop(struct kkk* st) // pop an item from the stack
{
float el;
if ((st->top) > 0)
{
st->top--;
el = st->elem[st->top];
return el;
}
else
{
printf("Stack is empty \n");
return 0;
}
}
float Vulychtop(struct kkk* st) // deleting the top of the stack
{
if ((st->top) > 0) {
return(st->elem[st->top - 1]);
}
else {
printf("Stack is empty!\n");
return 0;
}
}
int Gettop(struct kkk* st) // top element of the stack without delting
{
return(st->top);
}
int Isempty(struct kkk* st) // check
{
if ((st->top) == 0)
return 1;
else return 0;
}
void Vuvid(struct kkk* st) // Output of all elements
{
int i;
i = st->top;
if (Isempty(st) == 1) return;
do {
i--;
printf("%f\n", st->elem[i]);
} while (i > 0);
}
int main()
{
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
int i, n, k;
float znach;
element = (struct kkk*)malloc(sizeof(struct kkk));
Init(element);
printf("Enter the number of items in the stack \n");
scanf("%d", &n);
for (i = 0; i < n; i++) {
printf("Enter the number %d: ", i);
scanf("%f", &znach);
Push(element, znach);
}
printf("In stack %d elements \n", Gettop(element));
printf("\n");
Vuvid(element);
printf("Top element %f\n", Vulychtop(element));
do {
printf("The element to be removed %f, ", Pop(element));
printf("Items left in the stack %d \n", Gettop(element));
} while (Isempty(element) == 0);
return 0;
}
Result: https://i.stack.imgur.com/wLczr.png
I create a stack, after which I start entering numbers into it. With that, I'm fine. Next, I find the top element of the stack and pop it out. After that, I need to remove those numbers from the stack, the value of which is less than 10, and I manage to completely clear the stack one by one. Can't make a condition for this.
One approach would be to use another stack as temporary storage. In pseudo-code something like:
* create tmp-stack
* while org-stack isn't empty
* data = pop org-stack
* if (data >= 10) push data to tmp-stack
* while tmp_stack isn't empty
* data = pop tmp_stack
* push data to org-stack
* free tmp-stack
This can be implemented using the already existing function.
Better performance can be achieved by operating directly on the array holding the stacks data. Based on ideas from #SergeBallesta it may look like:
* write-index = 0;
* read-index = 0;
* while read-index < top
* if array[read-index] >= 10
* array[write-index] = array[read-index]
* write-index = write-index + 1
* read-index = read-index + 1
* top = write-index
It seems you are using an old MS compiler. For example the header <malloc.h> is not a standard C header. Instead you should use the header <stdlib.h>. Also neither declaration from the header <math.h> is used. So you may remove the inclusion of the header.
The function Gettop does not do what is written in the comment for the function
// top element of the stack without delting
Actually it returns the current value of the data member st->top that is how many elements are present in the stack.
On the other hand, the comment for the function Vulychtop
// deleting the top of the stack
is incorrect. The function does not delete an element from the stack because the value of the data member st->top is not decreased. Also such a function should not output any message. It is the function Pop that removes an element from the stack.
The function Vulychtop could be defined the following way
int Vulychtop( struct kkk *st, float *value )
{
if ( st->top != 0 ) *value = st->elem[st->top - 1];
return st->top != 0;
}
The function Pop also should not issue any message and return an element of the stack.
Also there is no great sense to declare the pointer element in the file scope. It could be declared in main.
The pointer st declared in the file scope
struct kkk* st, * element; // pointers
^^
is not used in the program.
Also there is no need to allocate an object of the type struct kkk dynamically.
You could just write in main
struct kkk element;
Init( &element );
To remove elements that are less than 10 from the original stack using the open interface of the stack you need an auxiliary stack.
For example
struct kkk st;
Init( &st );
while( !Isempty( element ) )
{
float value;
Vulychtop( element, &value ); // here I am using the function definition I showed above
if ( !( value < 10.0f ) ) Push( &st, value );
Pop( element );
}
while( !Isempty( &st ) )
{
float value;
Vulychtop( &st, &value ); // here I am using the function definition I showed above
Push( element, value );
Pop( &st );
}
You can yourself add messages in the while loops about what elements are popped, pushed or removed.
Pay attention that as you allocated an object of the type struct kkk dynamically you should free it before exiting the program
free( element );
I'm working on CS50's Week 5 assignment, Speller. I'm building my functions one at a time, and I'm running into problems with my unload function (Line 151). Right now, I'm just testing the iteration in a way that prints results before I use that iteration to free each of the nodes. I'm doing this by changing each node's word to "FREE" in the order these nodes are to be freed.
The function call (Line 60) returns true, and the printf command prints successfully. However, everything in the unload function itself is being ignored. None of the printf lines that I added to see its progress (DEBUG DEBUG DEBUG) are printing. The print() function call on line 63 should be printing the table with all of the words set to "FREE", and all dictionary word locations showing "NOT FOUND". Instead, it's printing the list and locations completely unaltered, and with none of the DEBUG print commands within the for loop (Line 155) triggering.
I don't understand why this is happening. The unload() function call alone, whether or not it returns true, should still at least trigger the first printf command in the for loop (Line 157). But even that is skipped.
Can someone please help me understand why the function is returning true, yet making none of the changes it's supposed to? Thanks in advance.
EDIT: Okay, I was told that I wasn't calling the unload function correctly on line 60. I've since corrected that. Now it will print out "LOCATION 00:", but it ends as soon as it hits that first while loop on line 158. I was having this problem before, and I'm not sure why it's doing this. strcmp() should see that the head node's word does not match "FREE" until it makes the change from the end of the list to the beginning. Why is the while loop not even triggering?
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
unsigned int HASH_MAX = 50; // Max elements in hash table
unsigned int LENGTH = 20; // Max length of word to be stored
unsigned int hash(const char *word); // assign hash code -- [(code + current letter) * 3] * string length, % HASH_MAX
bool load(FILE *dictionary); // load dictionary into memory
bool check(char *word); // check if word exists in dictionary
bool unload(void); // unload dictionary from memory, free memory (CURRENTLY DEBUGGING, CHECKING ITERATION)
void print(void); // print table contents and node locations
typedef struct _node // node structure: stored word, pointer to next node
{
char *word[20];
struct _node *next;
} node;
node *HASH_TABLE[50];
int main(int argc, char *argv[])
{
FILE *dictionary = fopen("C:/Users/aaron/Desktop/Dictionary.txt", "r"); // open dictionary file, read
if (!dictionary) // if dictionary is NULL, return error message, end program
{
printf("FILE NOT FOUND\n");
return 1;
}
if (load(dictionary)) // if dictionary loaded successfully (function call), close dictionary and print table contents
{
fclose(dictionary);
print(); // print "LIST (number): {(name, address), ...}\n
}
char *checkword = "Albatross"; // test check function for word that does not exist in the library
char *checkword2 = "Riku"; // test check function for word that does exist in the library
if (check(checkword)) // return check results for checkword, found or not found
{
printf("\n%s found\n", checkword);
}
else
{
printf("\n%s not found\n", checkword);
}
if (check(checkword2)) // return check results for checkword2, found or not found
{
printf("\n%s found\n", checkword2);
}
else
{
printf("\n%s not found\n", checkword2);
}
if (unload()) // if unloaded successfully (function call), print contents
{
printf("\nUNLOADED...\n\n"); // DEBUG DEBUG DEBUG (confirm unload function returned true)
print();
}
}
unsigned int hash(const char *word) // assign hash code -- [(code + current letter) * 3] * string length, % HASH_MAX
{
char word_conv[LENGTH + 1]; // store converted word for uniform key
unsigned int code = 0; // hash code
strcpy(word_conv, word);
for (int i = 0; i < strlen(word); i++) // set all letters in the word to lower case
{
word_conv[i] = tolower(word_conv[i]);
}
for (int j = 0; j < strlen(word_conv); j++) // for all letters in converted word, add ascii value to code and multiply by 3
{
code += word_conv[j];
code = code * 3;
}
code = code % HASH_MAX; // set code to remainder of current code divided by maximum hash table size
return code;
}
bool load(FILE *dictionary) // load dictionary into memory
{
char word[LENGTH+1]; // store next word in the dictionary
while (!feof(dictionary)) // until end of dictionary file
{
fscanf(dictionary, "%s", word); // scan for next word
node *new_n = malloc(sizeof(node)); // new node
strcpy(new_n->word, word); // store scanned word in new node
new_n->next = NULL; // new node's next pointer set to NULL
unsigned int code = hash(word); // retrieve and store hash code
if (HASH_TABLE[code] == NULL) // if hash location has no head
{
HASH_TABLE[code] = new_n; // set new node to location head
}
else if (HASH_TABLE[code] != NULL) // if head already exists at hash location
{
node *trav = HASH_TABLE[code]; // set traversal node
while (trav->next != NULL) // while traversal node's next pointer is not NULL
{
trav = trav->next; // move to next node
}
if (trav->next == NULL) // if traversal node's next pointer is null
{
trav->next = new_n; // set new node to traversal node's next pointer
}
}
}
return true; // confirm successful load
}
bool check(char *word) // check if word exists in dictionary
{
unsigned int code = hash(word); // retrieve and store hash code
node *check = HASH_TABLE[code]; // set traversal node to hash location head
while (check != NULL) // while traversal node is not NULL
{
int check_true = strcasecmp(check->word, word); // compare traversal node's word to provided word argument
if (check_true == 0) // if a match is found, return true
{
return true;
}
else if (check_true != 0) // if no match, move to next node
{
check = check->next;
}
}
if (check == NULL) // if end of list is reached without a match, return false
return false;
}
bool unload(void) // unload dictionary from memory, free memory (CURRENTLY DEBUGGING, CHECKING ITERATION)
{
char *word = "FREE"; // DEBUG DEBUG DEBUG (changin all nodes' words to "FREE" to test iteration)
for (int i = 0; i < HASH_MAX; i++) // for every element in the hash table, HASH_MAX (50)
{
printf("LOCATION %02d:\n", i); // DEBUG DEBUG DEBUG (print current hash table location)
while (strcmp(HASH_TABLE[i]->word, word) != 0) // while the head node's word is not "FREE"
{
node *trav = HASH_TABLE[i]; // set traversal node to head
printf("HEAD WORD: %s\n", HASH_TABLE[i]->word); // DEBUG DEBUG DEBUG (print head word to confirm while condition)
while (strcmp(trav->next->word, word) != 0) // while the traversal node's word is not "FREE"
{
trav = trav->next; // move to next node
printf("."); // DEBUG DEBUG DEBUG (print a dot for every location skipped)
}
printf("\n"); // DEBUG DEBUG DEBUG
strcpy(trav->word, word); // set traversal node's word to "FREE"
printf("{"); // DEBUG DEBUG DEBUG
while (trav != NULL) // DEBUG DEBUG DEBUG (print hash location's current list of words)
{
printf("%s, ", trav->word); // DEBUG DEBUG DEBUG
}
printf("}\n\n"); // DEBUG DEBUG DEBUG
}
}
return true; // freed successfully
}
void print(void) // print hash table contents and node locations
{
for (int i = 0; i < HASH_MAX; i++) // for every element in the hash table
{
node *check = HASH_TABLE[i]; // set traversal node to current hash table element head
printf("LIST %02d: {", i); // print hash table element location
while (check != NULL) // for all nodes in the current linked list
{
printf("%s, ", check->word); // print traversal node's word
check = check->next; // move to next node
}
printf("}\n");
}
printf("\n");
FILE *dictionary = fopen("C:/Users/aaron/Desktop/Dictionary.txt", "r"); // open dictionary file
while (!feof(dictionary)) // for all words in the dictionary
{
char word[LENGTH + 1]; // store next word
fscanf(dictionary, "%s", word); // scan for next word
unsigned int code = hash(word); // retrieve and store word's hash code
node *search = HASH_TABLE[code]; // set traversal node to hash location head
while (search != NULL) // for all nodes at that location, or until word is found
{
if (strcasecmp(search->word, word) == 0) // compare traversal node's word to scanned word (case insensitive)
{
printf("%s: %p\n", search->word, search); // print traversal node's word and location
break; // break while loop
}
else
{
search = search->next; // if traversal node's word does not match scanned word, move to next node
}
}
if (search == NULL) // if the scanned word matches none of the words in the hash location's linked list
printf("\"%s\" NOT FOUND\n", word); // word not found
}
fclose(dictionary); // close dictionary file
}
Caveat: chqrlie has pointed out many of the basic issues, but here's some refactored code.
Your main issue was that unload didn't actually remove the nodes.
One of things to note is that it's easier/faster/better to use tolower once per string.
If the lowercased version is what we store in the node, and we lowercase the search word in check, we can use strcmp instead of strcasecmp [which has to redo the lowercasing for both arguments on each loop iteration].
So, I've changed the hash function to lowercase its argument "in-place".
As I mentioned in my above comment, print was extraneously rereading the dictionary file. So, I've removed that code. If it were necessary to do this, it should go into [yet] another function, or load and/or check should be reused.
(i.e.) print should do one thing well [a programming maxim].
Personally, I dislike "sidebar" comments:
if (unload()) // if unloaded successfully (function call), print contents
I prefer the comment to go above the line:
// if unloaded successfully (function call), print contents
if (unload())
To me, this is much clearer and it helps prevent the line from going beyond 80 characters in width.
Certain fixed constants (e.g. HASH_MAX and LENGTH) are global variables. This prevents them from being used to define arrays
(e.g.) you couldn't say:
node *HASH_TABLE[HASH_MAX];
and had to "hardwire" it as:
node *HASH_TABLE[50];
If we define these with either #define or as an enum, then we can use the preferred definitions.
Doing something like:
for (int i = 0; i < strlen(word); i++)
increases the loop time from O(length) to O(length^2) because strlen is called "length" times inside the loop and it rescans the string each time.
Much better to do:
int len = strlen(word);
for (int i = 0; i < len; i++)
But even this has an extra scan of the buffer. It can be better is to do something like:
for (int chr = *word++; chr != 0; chr = *word++)
I've refactored the code with annotations for the bugs. Original code is bracketed inside a #if 0 block:
#if 0
// old/original code
#else
// new/refactored code
#endif
Anyway, here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#if 1
#include <ctype.h>
#endif
// Max elements in hash table
#if 0
unsigned int HASH_MAX = 50;
#else
enum {
HASH_MAX = 50
};
#endif
// Max length of word to be stored
#if 0
unsigned int LENGTH = 20;
#else
enum {
LENGTH = 20
};
#endif
// assign hash code -- [(code + current letter) * 3] * string length, % HASH_MAX
#if 0
unsigned int hash(const char *word);
#else
unsigned int hash(char *word);
#endif
// load dictionary into memory
bool load(FILE *dictionary);
// check if word exists in dictionary
#if 0
bool check(char *word);
#else
bool check(const char *word);
#endif
// unload dictionary from memory, free memory (CURRENTLY DEBUGGING,
// CHECKING ITERATION)
bool unload(void);
// print table contents and node locations
void print(void);
// node structure: stored word, pointer to next node
typedef struct _node {
#if 0
char *word[20];
#else
char word[LENGTH + 1];
#endif
struct _node *next;
} node;
#if 0
node *HASH_TABLE[50];
#else
node *HASH_TABLE[HASH_MAX];
#endif
int
main(int argc, char *argv[])
{
// open dictionary file, read
#if 0
FILE *dictionary = fopen("C:/Users/aaron/Desktop/Dictionary.txt", "r");
#else
FILE *dictionary = fopen("Dictionary.txt", "r");
#endif
// if dictionary is NULL, return error message, end program
if (!dictionary) {
printf("FILE NOT FOUND\n");
return 1;
}
// if dictionary loaded successfully (function call), close dictionary and
// print table contents
if (load(dictionary)) {
fclose(dictionary);
// print "LIST (number): {(name, address), ...}\n
print();
}
// test check function for word that does not exist in the library
char *checkword = "Albatross";
// test check function for word that does exist in the library
char *checkword2 = "Riku";
// return check results for checkword, found or not found
if (check(checkword)) {
printf("\n%s found\n", checkword);
}
else {
printf("\n%s not found\n", checkword);
}
// return check results for checkword2, found or not found
if (check(checkword2)) {
printf("\n%s found\n", checkword2);
}
else {
printf("\n%s not found\n", checkword2);
}
// if unloaded successfully (function call), print contents
if (unload()) {
// DEBUG DEBUG DEBUG (confirm unload function returned true)
printf("\nUNLOADED...\n\n");
print();
}
}
// assign hash code -- [(code + current letter) * 3] * string length, % HASH_MAX
unsigned int
hash(char *word)
{
// store converted word for uniform key
#if 0
char word_conv[LENGTH + 1];
#endif
// hash code
unsigned int code = 0;
#if 0
strcpy(word_conv, word);
// set all letters in the word to lower case
for (int i = 0; i < strlen(word); i++) {
word_conv[i] = tolower(word_conv[i]);
}
// for all letters in converted word, add ascii value to code and multiply by 3
for (int j = 0; j < strlen(word_conv); j++) {
code += word_conv[j];
code = code * 3;
}
#else
int chr;
while (1) {
chr = *word;
if (chr == 0)
break;
chr = tolower(chr);
*word++ = chr;
code += chr;
code *= 3;
}
#endif
// set code to remainder of current code divided by maximum hash table size
code = code % HASH_MAX;
return code;
}
// load dictionary into memory
bool
load(FILE * dictionary)
{
// store next word in the dictionary
char word[LENGTH + 1];
// until end of dictionary file
// NOTE/BUG: don't use feof
#if 0
while (!feof(dictionary)) {
// scan for next word
fscanf(dictionary, "%s", word);
#else
// scan for next word
while (fscanf(dictionary, "%s", word) == 1) {
#endif
// new node
node *new_n = malloc(sizeof(node));
// store scanned word in new node
strcpy(new_n->word, word);
// new node's next pointer set to NULL
new_n->next = NULL;
// retrieve and store hash code
unsigned int code = hash(new_n->word);
// NOTE/BUG: there's no need to append to the end of the list -- pushing
// on the front is adequate and is faster
#if 0
// if hash location has no head
if (HASH_TABLE[code] == NULL) {
// set new node to location head
HASH_TABLE[code] = new_n;
}
// if head already exists at hash location
else if (HASH_TABLE[code] != NULL) {
// set traversal node
node *trav = HASH_TABLE[code];
// while traversal node's next pointer is not NULL
while (trav->next != NULL) {
// move to next node
trav = trav->next;
}
// if traversal node's next pointer is null
if (trav->next == NULL) {
// set new node to traversal node's next pointer
trav->next = new_n;
}
}
#else
new_n->next = HASH_TABLE[code];
HASH_TABLE[code] = new_n;
#endif
}
// confirm successful load
return true;
}
// check if word exists in dictionary
#if 0
bool
check(char *word)
#else
bool
check(const char *arg)
#endif
{
char word[LENGTH + 1];
// retrieve and store hash code
#if 1
strcpy(word,arg);
#endif
unsigned int code = hash(word);
// set traversal node to hash location head
node *check = HASH_TABLE[code];
// while traversal node is not NULL
while (check != NULL) {
// compare traversal node's word to provided word argument
// NOTE/BUG: strcmp is faster than strcasecmp if we convert to lowercase _once_
#if 0
int check_true = strcasecmp(check->word, word);
#else
int check_true = strcmp(check->word, word);
#endif
#if 0
// if a match is found, return true
if (check_true == 0) {
return true;
}
// if no match, move to next node
else if (check_true != 0) {
check = check->next;
}
#else
if (check_true == 0)
return true;
check = check->next;
#endif
}
// if end of list is reached without a match, return false
#if 0
if (check == NULL)
return false;
#else
return false;
#endif
}
// unload dictionary from memory, free memory
// (CURRENTLY DEBUGGING, CHECKING ITERATION)
bool
unload(void)
{
// DEBUG DEBUG DEBUG (changin all nodes' words to "FREE" to test iteration)
#if 0
char *word = "FREE";
#endif
// for every element in the hash table, HASH_MAX (50)
for (int i = 0; i < HASH_MAX; i++) {
#if 0
// DEBUG DEBUG DEBUG (print current hash table location)
printf("LOCATION %02d:\n", i);
// while the head node's word is not "FREE"
while (strcmp(HASH_TABLE[i]->word, word) != 0) {
// set traversal node to head
node *trav = HASH_TABLE[i];
// DEBUG DEBUG DEBUG (print head word to confirm while condition)
printf("HEAD WORD: %s\n", HASH_TABLE[i]->word);
// while the traversal node's word is not "FREE"
while (strcmp(trav->next->word, word) != 0) {
// move to next node
trav = trav->next;
// DEBUG DEBUG DEBUG (print a dot for every location skipped)
printf(".");
}
// DEBUG DEBUG DEBUG
printf("\n");
// set traversal node's word to "FREE"
strcpy(trav->word, word);
// DEBUG DEBUG DEBUG
printf("{");
// DEBUG DEBUG DEBUG (print hash location's current list of words)
while (trav != NULL) {
// DEBUG DEBUG DEBUG
printf("%s, ", trav->word);
}
// DEBUG DEBUG DEBUG
printf("}\n\n");
}
#else
node *nxt;
for (node *cur = HASH_TABLE[i]; cur != NULL; cur = nxt) {
nxt = cur->next;
free(cur);
}
HASH_TABLE[i] = NULL;
#endif
}
// freed successfully
return true;
}
// print hash table contents and node locations
void
print(void)
{
// for every element in the hash table
for (int i = 0; i < HASH_MAX; i++) {
// set traversal node to current hash table element head
node *check = HASH_TABLE[i];
// print hash table element location
printf("LIST %02d: {", i);
// for all nodes in the current linked list
while (check != NULL) {
// print traversal node's word
printf("%s, ", check->word);
// move to next node
check = check->next;
}
printf("}\n");
}
printf("\n");
// NOTE/BUG: why reread dictionary after printing it?
#if 0
// open dictionary file
FILE *dictionary = fopen("C:/Users/aaron/Desktop/Dictionary.txt", "r");
// for all words in the dictionary
while (!feof(dictionary)) {
// store next word
char word[LENGTH + 1];
// scan for next word
fscanf(dictionary, "%s", word);
// retrieve and store word's hash code
unsigned int code = hash(word);
// set traversal node to hash location head
node *search = HASH_TABLE[code];
// for all nodes at that location, or until word is found
while (search != NULL) {
// compare traversal node's word to scanned word (case insensitive)
if (strcasecmp(search->word, word) == 0) {
// print traversal node's word and location
printf("%s: %p\n", search->word, search);
// break while loop
break;
}
else {
// if traversal node's word does not match scanned word,
// move to next node
search = search->next;
}
}
// if the scanned word matches none of the words in the hash location's
// linked list
if (search == NULL)
// word not found
printf("\"%s\" NOT FOUND\n", word);
}
// close dictionary file
fclose(dictionary);
#endif
}
Here's a version that has the #if 0 blocks removed.
Also, I've added a slight reordering in the load function, so that it inputs the data directly into the final place inside the node element (i.e. eliminates the intermediate buffer and a strcpy)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
// Max elements in hash table
enum {
HASH_MAX = 50
};
// Max length of word to be stored
enum {
LENGTH = 20
};
// assign hash code -- [(code + current letter) * 3] * string length, % HASH_MAX
unsigned int hash(char *word);
// load dictionary into memory
bool load(FILE *dictionary);
// check if word exists in dictionary
bool check(const char *word);
// unload dictionary from memory, free memory (CURRENTLY DEBUGGING,
// CHECKING ITERATION)
bool unload(void);
// print table contents and node locations
void print(void);
// node structure: stored word, pointer to next node
typedef struct _node {
char word[LENGTH + 1];
struct _node *next;
} node;
node *HASH_TABLE[HASH_MAX];
int
main(int argc, char *argv[])
{
// open dictionary file, read
FILE *dictionary = fopen("Dictionary.txt", "r");
// if dictionary is NULL, return error message, end program
if (!dictionary) {
printf("FILE NOT FOUND\n");
return 1;
}
// if dictionary loaded successfully (function call), close dictionary and
// print table contents
if (load(dictionary)) {
fclose(dictionary);
// print "LIST (number): {(name, address), ...}\n
print();
}
// test check function for word that does not exist in the library
char *checkword = "Albatross";
// test check function for word that does exist in the library
char *checkword2 = "Riku";
// return check results for checkword, found or not found
if (check(checkword)) {
printf("\n%s found\n", checkword);
}
else {
printf("\n%s not found\n", checkword);
}
// return check results for checkword2, found or not found
if (check(checkword2)) {
printf("\n%s found\n", checkword2);
}
else {
printf("\n%s not found\n", checkword2);
}
// if unloaded successfully (function call), print contents
if (unload()) {
// DEBUG DEBUG DEBUG (confirm unload function returned true)
printf("\nUNLOADED...\n\n");
print();
}
}
// assign hash code -- [(code + current letter) * 3] * string length, % HASH_MAX
unsigned int
hash(char *word)
{
// store converted word for uniform key
// hash code
unsigned int code = 0;
unsigned char chr;
while (1) {
chr = *word;
if (chr == 0)
break;
chr = tolower(chr);
*word++ = chr;
code += chr;
code *= 3;
}
// set code to remainder of current code divided by maximum hash table size
code = code % HASH_MAX;
return code;
}
// load dictionary into memory
bool
load(FILE *dictionary)
{
// scan for next word
while (1) {
// new node
node *new_n = malloc(sizeof(node));
if (fscanf(dictionary, "%s", new_n->word) != 1) {
free(new_n);
break;
}
// store scanned word in new node
new_n->next = NULL;
// retrieve and store hash code
unsigned int code = hash(new_n->word);
// pushing on the front of the list is adequate and is faster
new_n->next = HASH_TABLE[code];
HASH_TABLE[code] = new_n;
}
// confirm successful load
return true;
}
// check if word exists in dictionary
bool
check(const char *arg)
{
char word[LENGTH + 1];
// retrieve and store hash code
strcpy(word,arg);
unsigned int code = hash(word);
// set traversal node to hash location head
node *check = HASH_TABLE[code];
// while traversal node is not NULL
while (check != NULL) {
// compare traversal node's word to provided word argument
int check_true = strcmp(check->word, word);
if (check_true == 0)
return true;
check = check->next;
}
// if end of list is reached without a match, return false
return false;
}
// unload dictionary from memory, free memory
// (CURRENTLY DEBUGGING, CHECKING ITERATION)
bool
unload(void)
{
// for every element in the hash table, HASH_MAX (50)
for (int i = 0; i < HASH_MAX; i++) {
node *nxt;
for (node *cur = HASH_TABLE[i]; cur != NULL; cur = nxt) {
nxt = cur->next;
free(cur);
}
HASH_TABLE[i] = NULL;
}
// freed successfully
return true;
}
// print hash table contents and node locations
void
print(void)
{
// for every element in the hash table
for (int i = 0; i < HASH_MAX; i++) {
// set traversal node to current hash table element head
node *check = HASH_TABLE[i];
// print hash table element location
printf("LIST %02d: {", i);
// for all nodes in the current linked list
while (check != NULL) {
// print traversal node's word
printf("%s, ", check->word);
// move to next node
check = check->next;
}
printf("}\n");
}
printf("\n");
}
UPDATE:
Could you please explain for (int chr = *word++; chr != 0; chr = *word++)? I don't know what *word++ means in this context.
Sure. With chr = *word++; it means dereference word [a char pointer]. This fetches the char value pointed to by word (i.e. fetch the value from memory). Then, set this value into chr. Then, increment word [so it points to the next character in the array.
The statement is composed of three operators: = is the assignment operator. * is a dereference operator and ++ is a post-decrement operator.
Based on the precedence [and/or binding] of the operators, * has higher precedence [tighter binding], so it is performed first. The value is placed in chr. Then, ++ is performed on the value in word. It is as the following is performed as a single statement:
chr = *word;
word += 1;
chr = tolower(chr); should be chr = tolower((unsigned char)chr); for reasons explained in my answer. Alternatively, you could define chr as unsigned char chr;
I was under the impression that tolower et. al. were "self protective" of this (e.g. they did the unsigned char cast). But, the [linux] manpage says its UB if the value is out of range. I've edited the second example to use unsigned char chr;.
Strangely, for glibc's tolower, it has a range check built it that works on the int value and returns the original value (i.e. does not index into the translation table) if the value is out of range. This appears to be part of some BSD compatibility [the BSD manpage states it does a range check, but the feature is deprecated]. I'm guessing the glibc range check as added after the manpage was written.
To me, the macro should just do the cast itself [and the global function as well]. But, I think this might break the BSD compatibility.
But, now we're all hamstrung to the old way [or add a wrapper macro] because of backward compatibility.
it is confusing for hash to have a side effect on its argument and further confusing that this side effect be necessary for the strcmp in check to work.
The side effect is [probably] no more [or, perhaps, even less] egregious than what strtok does. That is, it's not modifying a hidden/unrelated global, etc.
IMO, it wouldn't be confusing if the effect were commented [I documented it in the answer text]. Perhaps renaming hash to something a bit more descriptive would help. We could do: take_hash_of_argument_that_we_modify_to_lowercase_first.
That would make the function name "self documenting" as some (e.g. "Uncle" Bob Martin(?)) might suggest member functions should be.
But, maybe hash_and_lowercase might be better. This might be a sufficient clue to the reader that they need to consult the API documentation for the function rather than assuming they know all about it from just the name.
The linked list traversal is much faster with strcmp, so, at a minimum [architecturally] we want to store lower case strings in the nodes. We don't want to repeat the lowercasing for each node on each scan. And, we don't want strcasecmp to repeat the lowercasing on word [and the string in the node] for each loop iteration.
As you say, we could have two functions. And we could still achieve this refactoring: a string based version of tolower that lowercases its argument and leave the hash as it was done originally.
Originally, I considered this approach. I soon realized that everywhere you did a hash, you wanted it to be on the lowercased string. We could achieve this with (e.g.):
strlower(word);
value = hash(word);
But, there wasn't a use case here for doing one of these calls separately--only in pairs.
So, given that, why scan the argument string twice and slow down the operation by 2x?
From JFK [after the failed Bay of Pigs invasion]: Mistakes are not errors if we admit them.
So, I'd paraphrase that as: Side effects are not errors if we document them.
There are multiple problems in your code:
the word member of the _node structure has the wrong type: it should just be an array of 20 characters, not an array of 20 char pointers. And dont use _node, identifiers starting with _ are reserved. Change the definition to:
typedef struct node { // node structure: stored word, pointer to next node
char word[LENGTH+1];
struct node *next;
} node;
your reading loops are incorrect: while (!feof(dictionary)) is not the proper test to detect the end of file, you should instead test if fscanf() successfully reads the next word:
while (fscanf(dictionary, "%s", word) == 1) // until end of dictionary file
Furthermore you should specify a maximum length for fscanf() to avoid undefined behavior on long words:
while (fscanf(dictionary, "%19s", word) == 1) // read at most 19 characters
You do not check for allocation failure.
There are many redundant tests such as else if (HASH_TABLE[code] != NULL) and if (trav->next == NULL) in load(), else if (check_true != 0) and if (check == NULL) in check().
You do not modify trav in the loop while (trav != NULL) in the DEBUG code, causing an infinite loop.
It is not difficult to free the dictionary in unload(), your iteration checking code is way too complicated, you already have correct iteration code for print(). Here is a simple example:
bool unload(void) { // unload dictionary from memory, free memory
for (int i = 0; i < HASH_MAX; i++) {
while (HASH_TABLE[i]) {
node *n = HASH_TABLE[i];
HASH_TABLE[i] = n->next;
free(n);
}
}
return true;
}
Note also that there is no need to store the converted word to compute the hash value, and char values must be cast as (unsigned char) to pass to tolower() because this function is only defined for the values of unsigned char and the special negative value EOF. char may be a signed type, so tolower(word[i]) has undefined behavior for extended characters.
unsigned int hash(const char *word) // assign hash code -- [(code + current letter) * 3] * string length, % HASH_MAX
{
unsigned int code = 0; // hash code
for (int i = 0; word[i] != '\0'; i++) {
// compute hashcode from lowercase letters
code = (code + tolower((unsigned char)word[i])) * 3;
}
code = code % HASH_MAX; // set code to remainder of current code divided by maximum hash table size
return code;
}
Hey I am writing a stack function and I am having trouble with popping values off the top of my stack. My push functions seems to be working though. This is the code for my push and pop.
void push(int num, int ** sp)
{
if (++(*sp) == NULL)
printf("Stack Overflow");
else{
printf("sp for push = %p \n", *sp);
**sp++ = num;
}
}
int pop(int **sp)
{
printf("sp for pop = %p\n", *sp);
num = (**sp)--;
printf("sp = %d\n",num);
printf("sp for pop = %p\n", *sp);
return num;
}
I was also having trouble checking for the null condition to prevent popping elements that are not even on the stack, but one problem at a time.
Anyway, the output of the function looks like this when I push 15 then 5
sp for push = 0x1761014
sp for push = 0x1761018
sp for pop = 0x1761018
sp = 5
sp for pop = 0x1761018
5
Where sp is the stack pointer. Notice how the stack pointer increments by the sizeof int. I am passing both pointers by reference, so they should both change the position in memory they are pointing too, but for the pop function when I am post-decrementing the pointer the value is not changing for some reason. Can someone help explain this to me? Any help is appreciated.
Basic mistake, I needed to change the address of the pointer pointing to the data, not the pointer pointing to the pointer. The new code is:
int pop(int **sp)
{
printf("sp for pop = %p\n", *sp);
num = (**sp);
(*sp)--;
printf("sp = %d\n",num);
printf("sp for pop = %p\n", *sp);
return num;
}
In pop, you are decrementing the value in stack rather than the pointer to it. Remember, *sp is the stack pointer, NOT **sp. So, you need to decrement *sp and not **sp.
int pop(int **sp)
{
printf("sp for pop = %p\n", *sp);
/* num = (**sp)--; */ // ISSUE: Decrementing value instead of pointer
num = *(*sp)--;
printf("sp = %d\n",num);
printf("sp for pop = %p\n", *sp);
return num;
}
There is also an issue in push function. Althouh it does not create issue now, it is best to fix it.
void push(int num, int ** sp)
{
if (++(*sp) == NULL)
printf("Stack Overflow");
else{
printf("sp for push = %p \n", *sp);
// **sp++ = num; // ISSUE: increment not needed.
// Once incremented, it will NOT point
// to stack pointer anymore
**sp = num;
}
}
To check if you have space in the stack while pushing, and to check if any elements are there while popping, you can use an index variable to keep track of number of elements in stack.
Suppose Max capacity of stack is say N. Initialize the index to 0.
Now whenever you push, first check if index < N, then only push ( and increment the index by 1). If index is not smaller than N, then you have stackoverflow.
Whenever you pop, first check if index > 0, then only pop (and decrement the index by 1). If index is not greater than 0, then you have no element to pop.
Also, worth mentionning, is that it is more natural if this index is named stack pointer - sp - which points to the top of stack. You can can pass the pointer to base of stack and call it base or something else. Your functions would then be:
void push(int num, int *base, int sp) { ... } // base: start address of stack,
int pop(int *base, int sp) { ... } // sp: index of top of stack
So for some reason this function appears to be pushing all of the same value onto the stack (or just printing the same value; i suppose it could actually be something to do with the printAll function which i also included.)? The thing is, for the element array (which is an array of integers), printAll appropriately cycles through the values. But anything concerning the charElement array, the printAll function ONLY prints the most recent value for each and every value in the charElements function. Any idea why this is happening?
void pushString(Stack *S, char element[])
{
/* If the stack is full, we cannot push an element into it as there is no space for it.*/
if(S->size == S->capacity)
{
printf("Stack is Full\n");
}
else
{
/* Push an element on the top of it and increase its size by one*/
S->charElements[S->size++] = element;
S->elements[S->size-1] = '\n';
}
return;
}
void printAll(Stack *S)
// NOTE: Called w/ user input 'f'
// PRE: Stack S is initialized
// POST: Print all values from the stack
// Do NOT alter anything
{
int i;
for(i = 0; i < S->size; i++)
{
printf("%d \n", i);
if(S->charElements[i] == "\n")
{
printf("%.2f \n", (float)S->elements[i]);
}
else if(S->elements[i] == '\n')
{
printf("%s \n", S->charElements[i]);
}
}
}
S->charElements[S->size++] = element;
merely copies the pointer to the passed-in char* element.
If you are using the same buffer to read the input and then pass it to the pushString function repeatedly, all stack elements end up pointing to the same buffer, and its contents is the last entered value.
You need to copy the contents, using strdup or malloc and strcpy.
S->charElements[S->size++] = strdup(element);
resp.
S->charElements[S->size] = malloc(strlen(element) + 1);
// check for NULL
strcpy(S->charElements[S->size++], element);
The code is very strange. Elements are represented by char [] in the push function, but printed using %f in printAll(). This doesn't make a lot of sense, to me.
Without the declaration of the Stack structure, it's hard to follow the code's intentions.