hard linked list in C - c

I cant understand how works AddToList, if gHeadPtr point always to the first (minimal) rating structure I understand it, but gHeadPtr not point to it, or i mistake with that? Or somebody can tell me how works AddToList? I not know what mean it last string, why we need double pointer and on what struct point gHeadPtr and when gHeadPtr point to the first (minimal) rating structure, when to the struct we just add(with max rating)
struct DVDInfo
{
char rating;
char title[ kMaxTitleLength ];
char comment[ kMaxCommentLength ];
struct DVDInfo *prev;
struct DVDInfo *next;
};
char GetCommand( void );
struct DVDInfo *ReadStruct( void );
void AddToList( struct DVDInfo *curPtr );
void ListDVDs( bool forward );
char *TrimLine( char *line );
struct DVDInfo *gHeadPtr, *gTailPtr;
int main (int argc, const char * argv[])
{
char command;
while ( (command = GetCommand() ) != 'q' )
{
switch( command )
{
case 'n':
AddToList( ReadStruct() );
break;
case 'l':
case 'r':
ListDVDs( command=='l' );
break;
}
printf( "\n----------\n" );
}
printf( "Goodbye...\n" );
return 0;
}
char GetCommand( void )
{
char buffer[ 100+1 ];
printf( "Enter command (q=quit, n=new, l=list, r=reverse list): " );
fgets( buffer, sizeof(buffer), stdin );
return *TrimLine( buffer );
}
struct DVDInfo *ReadStruct( void )
{
struct DVDInfo *infoPtr;
infoPtr = malloc( sizeof( struct DVDInfo ) );
if ( infoPtr == NULL )
{
printf( "Out of memory!!! Goodbye!\n" );
exit( 1 );
}
char buffer[ 500+1 ];
printf( "Enter DVD Title: " );
fgets( buffer, sizeof(buffer), stdin );
strlcpy( infoPtr->title, TrimLine( buffer ), sizeof(infoPtr->title) );
printf( "Enter DVD Comment: " );
fgets( buffer, sizeof(buffer), stdin );
strlcpy( infoPtr->comment, TrimLine( buffer ), sizeof(infoPtr->comment) );
int num;
do
{
printf( "Enter DVD Rating (1-10): " );
fgets( buffer, sizeof(buffer), stdin );
num = atoi( TrimLine( buffer ) );
}
while ( ( num < 1 ) || ( num > 10 ) );
infoPtr->rating = num;
return( infoPtr );
}
void AddToList( struct DVDInfo *curPtr )
{
struct DVDInfo **nextPtrPtr = &gHeadPtr;
struct DVDInfo *prevPtr = NULL;
while ( *nextPtrPtr != NULL && curPtr->rating > (*nextPtrPtr)->rating )
{
prevPtr = *nextPtrPtr;
nextPtrPtr = &(prevPtr->next);
}
curPtr->prev = prevPtr; // link to previous struct
curPtr->next = *nextPtrPtr; // link to next struct
if ( curPtr->next != NULL )
curPtr->next->prev = curPtr; // link prev of next struct to curPtr
else
gTailPtr = curPtr; // no next struct: curPtr is now the tail
*nextPtrPtr = curPtr; // link next or previous struct (or head) to curPtr
} //когда функция передах структкру, а потом получает новую, указатели сохраняются?
void ListDVDs( bool forward )
{
struct DVDInfo *curPtr = ( forward ? gHeadPtr : gTailPtr );
bool separator = false;
if ( curPtr == NULL )
{
printf( "No DVDs have been entered yet...\n" );
}
else
{
while ( curPtr != NULL )
{
if ( separator )
printf( "--------\n" );
printf( "Title: %s\n", curPtr->title );
printf( "Comment: %s\n", curPtr->comment );
printf( "Rating: %d\n", curPtr->rating );
curPtr = ( forward ? curPtr->next : curPtr->prev );
separator = true;
}
}
}
char *TrimLine( char *line )
{
size_t length = strlen( line );
while ( length > 0 && isspace( line[length-1] ))
{
line[length-1] = '\0';
length--;
}
return line + strspn( line, " \t" );
}

struct DVDInfo **nextPtrPtr = &gHeadPtr;
struct DVDInfo *prevPtr = NULL;
The nextPtrPtr is required since the programmer does not want to mess with the global Head Pointer (gHeadPtr). We are simply using this pointer to iterate through the list as using a Pointer to Pointer is better than using the Head Pointer itself for iteration.
while ( *nextPtrPtr != NULL && curPtr->rating > (*nextPtrPtr)->rating )
{
prevPtr = *nextPtrPtr;
nextPtrPtr = &(prevPtr->next);
}
The code above from AddToList deals with finding out the location where the new node should go inside the List (ie descending rating of movies).
curPtr->prev = prevPtr; // link to previous struct
curPtr->next = *nextPtrPtr; // link to next struct
Lines above are used for insertion into the Linked List.
if ( curPtr->next != NULL )
curPtr->next->prev = curPtr; // link prev of next struct to curPtr
else
gTailPtr = curPtr;
These lines above are used if the new node (curPtr) is the first node or the last node in the linked list.

struct DVDInfo **nextPtrPtr = &gHeadPtr;
...
...
*nextPtrPtr = curPtr;
The value of a double pointer is address of another pointer. For AddToList(), you need double pointer because that way the value pointed by the nextPtrPtr automatically starts with the address of the list header (gHeadPtr) and if needed, updates it. If there was no need to update the gHeadPtr, you could have easily used "struct DVDInfo *nextPtrPtr".
For AddToList(), there are two cases and we definitely need the double pointer for the first case. The first case is that if gHeadPtr is NULL and that means there is no elements in the list. For such cases, "**nextPtrPtr = &gHeadPtr" would mean that hte value of nextPtrPtr would be NULL. That is what we are checking with "*nextPtrPtr != NULL". Since it is NULL, it would skip the while loop and *nextPtrPtr would point to curPtr". Thus, gHeadPtr would start to point to curPtr. The secodn case is that if the head (gHeadPtr) is not NULL, then we would enter the while loop and the nextPtrPtr would point to the last element based on the rating criteria. Due to that the curPtr would be added as the node after the nextPtrPtr node.
To further illustrate this, let us say AddToList() used a single pointer and the gHeadPtr
was NULL (I am providing the following code, which makes nextPtrPtr a pointer for the
sake of explanation). In this case, the nextPtrPtr would point to gHeadPtr and that means, it would
take the address of gHeadPtr (let us say 0x1010). Because nextPtrPtr is NULL (you probably
should initialize gHeadPtr with NULL, btw), it would now skip the while loop and the last
statement would be executed "nextPtrPtr = curPtr;". With this, nextPtrPtr now points to curPtr (let
us say has address 0x2020) -- this way, the gHeadPtr never got updated to point to
0x2020.
/* Note: Incorrect version for the sake of explanation */
void AddToList( struct DVDInfo *curPtr ) {
struct DVDInfo *nextPtrPtr = gHeadPtr;
struct DVDInfo *prevPtr = NULL;
while (nextPtrPtr != NULL && curPtr->rating > (nextPtrPtr)->rating ) {
prevPtr = nextPtrPtr;
nextPtrPtr = prevPtr->next;
}
curPtr->prev = prevPtr; // link to previous struct
curPtr->next = nextPtrPtr; // link to next struct
if ( curPtr->next != NULL )
curPtr->next->prev = curPtr; // link prev of next struct to curPtr
else
gTailPtr = curPtr; // no next struct: curPtr is now the tail
nextPtrPtr = curPtr; // link next or previous struct (or head) to curPtr
}

Related

Adding items to a linked list

I'd like to add an element to a list of element. My list is a struct containing a double, an integer and a pointer to the next element. Could someone tell me how to do the Add function please
#include <stdio.h>
#include <stdlib.h>
typedef struct Liste Liste;
struct Liste{
double c;
int n;
Liste* next; // pointe sur l'élément suivant
};
void Add(Liste array, Liste item) {
Liste* last = array.next;
while (last != NULL) {
last = last->next;
}
array.next = &item;
printf("%p\n", array.next);
}
int main(){
Liste array = {12.4, 4, NULL};
printf("%f\n", array.c);
Liste item = {15.4, 7, NULL};
Add(array, item);
printf("%p\n", array.next);
return 0;
}
Pass-by-value
In Add, C makes a copy of all the function parameters; their scope is the function itself. When one returns, the function parameters are popped from the stack and there is no way to get them back, as you have seen. The way to mutate structures is to pass a pointer to the structure, then modify that pointer using the structure pointer dereference operator, (arrow ->.)
Design
The reason one would use a linked-list is it is very cheap to reorder it, but the head of your linked-list is fixed, so you can't change it. You might change this by delineating the container, the list itself, from the contents. This is similar to using a double-pointer, but I think less confusing.
struct Noeud {
double c;
int n;
struct Noeud* next; // pointe sur l'élément suivant
};
struct Liste {
struct Noeud *tete; // singly-linked-list est defini par un pointer seul
};
Then you can add, (I've included assert.h.)
/* `O(n)` */
static void AddQueue(struct Liste *liste, struct Noeud *item) {
assert(liste && item && item->next == NULL);
struct Noeud* last = liste->tete;
if(last == NULL) { // case spécieux
liste->tete = item;
} else {
while (last->next != NULL) {
last = last->next;
}
last->next = item;
}
}
However, it's much simpler and asymptotically faster to add at the beginning of the list.
Pointerstructures like a linked list are powerful tools with a wide rage of application.
But first you have to understand pointers.
A pointer is a datastructure which contains the address of a datastructure.
Whenever you call a function the arguments of it are copied (pushed) to the stack.
If the arguments require a lot of storage space you use a pointer instead.
the code below uses pointers to create a linked list
#include "stdio.h"
#include "stdlib.h"
#include "stdbool.h"
typedef struct List List;
struct List{
double c;
int n;
List *next;
};
void AddItemEnd( List *RootItem, List *Item )
{
List *Last = RootItem;
while( Last->next != NULL )
{
Last = Last->next;
}
Last->next = Item;
}
void AddItemAtPos( List *RootItem, List *Item, unsigned int Pos )
{
if( Pos == 0 )
{
Item->next = RootItem;
}
else
{
List *TempItem = RootItem;
for( unsigned int i = 1; i < Pos && TempItem->next != NULL; ++i )
{
TempItem = TempItem->next;
}
Item->next = TempItem->next;
TempItem->next = Item;
}
}
void RemoveItemAtPos( List *RootItem, unsigned int Pos )
{
if( Pos == 0 )
{
free( (void*) RootItem );
}
else
{
List *TempItem = RootItem;
for( unsigned int i = 1; i < Pos && TempItem->next != NULL; ++i )
{
TempItem = TempItem->next;
}
if( TempItem->next == NULL )
{
return;
}else if( TempItem->next->next != NULL )
{
List *ItemToDelete = TempItem->next;
TempItem->next = TempItem->next->next;
free( (void*) ItemToDelete );
}else
{
free( (void*) TempItem->next );
TempItem->next =NULL;
}
}
}
int main(void) {
List *RootItem = malloc( sizeof( List ));
RootItem->c = 12.4;
RootItem->n = 4;
RootItem->next = NULL;
List *Item1 = malloc( sizeof(List ));
Item1->c = 15.4;
Item1->n = 7;
Item1->next = NULL ;
AddItemEnd( RootItem, Item1 );
List *IterationItem;
printf( "List created with AddItemEnd()\n\n" );
for( IterationItem = RootItem; IterationItem != NULL; IterationItem = IterationItem->next )
{
printf( "c: %lf\nn: %d\n\n", IterationItem->c, IterationItem->n );
}
List *item2 = malloc( sizeof( List ));
item2->c = 23.4;
item2->n = 1846;
item2->next = NULL ;
AddItemAtPos( RootItem, item2, 1 );
printf( "\n\nList extended with AddItemAtPos()\n\n");
for( IterationItem = RootItem; IterationItem != NULL; IterationItem = IterationItem->next )
{
printf( "c: %lf\nn: %d\n\n", IterationItem->c, IterationItem->n );
}
RemoveItemAtPos(RootItem, 1 );
printf( "\n\nList after RemoveItemAtPos()\n\n");
for( IterationItem = RootItem; IterationItem != NULL; IterationItem = IterationItem->next )
{
printf( "c: %lf\nn: %d\n\n", IterationItem->c, IterationItem->n );
}
free( (void*) RootItem );
free( (void*) item2 );
return 0;
}
The key elements when dealing with lists is pointers
and using memory allocation.
If we disregard your add function and just do a simple
example you will probably get the geist of it.
First allocate you starting list like this
Liste* array = malloc(sizeof(Liste));
Now you have one uninitialized block of memory
that array points to. You then need to initialize
it.
array->c = 12.4;
array->n = 4;
array->next = NULL;
in order to add a new entry to your list you
need to again allocate memory for the next node and
initialize it plus set the previous node next pointer
to point to it i.e. array->next.
Liste* item = malloc(sizeof(Liste));
item->c = 15.4;
item->n = 7;
item->next = NULL;
array->next = item;
now you have a list of two elements where array points
to the first
printing your short list
Liste* p = array;
while (p != NULL)
{
printf("%lf %d %p\n", p->c, p->n, p->next);
p = p->next;
}
So your Add functions does not allocate memory and copies
the parameters so that is not going to work.
Your Add function should have a pointer either to either the first or last item in your list e.g.
void Add(Liste* start, double c, int n)
Then you do as I showed you above and create a new node and assign the values
If you want to be able to pass an empty list to Add then you need to do differently, since start is copied it cannot be changed, you need to pass the address of the pointer
void Add(List** start, double c, int n)
{
Liste* node = malloc(sizeof(Liste));
...
(* put node in the list *)
if (*start == NULL)
{
*start = node; // first
}
else
{
(* find last node, see print loop *)
(* once you have last item, set it to point to node)
}
...
}
int main()
{
Liste* start = NULL;
Add(&start, 12.4, 4);
Add(&start, 15.4, 7);
...

Sorting with linked lists using the qsort algorithm

I'm still learning how linked lists works and I'm kinda struggling with the sorting using qsort algorithm and the nodes .
This is what I did so far .
So I'm having a crash somewhere in the code and I don't know if this qsort algorithm works this way with the linked lists or not.
Code Updated
void swapString(char **str1, char **str2)
{
char *temp = *str2;
*str2 = *str1;
*str1 = temp;
}
TCD *partition(TCD *Start, TCD *End, int (*cmp)(const void *, const void*))
{
TCD *partitionIdx = Start;
TCD *i ;
for (i = Start; i != End; i=i->Next)
{
if (cmp(i->Titel, End->Titel) < 0)
{
swapString(&i->Titel, &partitionIdx->Titel);
partitionIdx->Prev = partitionIdx;
partitionIdx = partitionIdx->Next;
}
}
swapString(&partitionIdx->Titel, &End->Titel);
return partitionIdx;
}
void Quicksort(TCD *Start, TCD *End, int (*cmp)(const void *, const void *))
{
if (Start !=NULL && End != Start && End!= Start->Next)
{
TCD *partitionIdx = partition(Start, End, cmp);
Quicksort(Start, partitionIdx->Prev, cmp);
Quicksort(partitionIdx->Next, End, cmp);
}
}
By the way , this is the definition of TCD
typedef struct F
{
char *Titel;
struct F *Next;
struct F *Prev;
}TCD;
There are several problems with your code:
The line partitionIdx->Prev = partitionIdx; does not make sense. It causes a node to point to itself. This cannot be correct. The purpose of a linked list is for a node to point to the next node and the previous node, but never to itself.
Your function partition is crashing because its parameter Start will sometimes point to a place in the linked list beyond the End parameter. This is because you call the function Quicksort without ensuring that its Start parameter does not point to a place beyond the End parameter.
The if condition if ( Start != NULL && End != Start && End != Start->Next ) does not make sense. The sub-expression End != Start->Next tests if the size of the partition is 2. If that is the case, the partition is not processed. However, a partition of size 2 must be sorted, so it must be processed. Only if the size is 1 should it not be processed.
I have changed the code of your algorithm by fixing the issues mentioned above, and it seems to work now. Also, I have added some functions to test the algorithm. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef struct F
{
char *Titel;
struct F *Next;
struct F *Prev;
} TCD;
void swapString( char **str1, char **str2 )
{
char *temp = *str2;
*str2 = *str1;
*str1 = temp;
}
TCD *partition( TCD *Start, TCD *End, int( *cmp )(const void *, const void*) )
{
TCD *partitionIdx = Start;
TCD *i;
for ( i = Start; i != End; i = i->Next )
{
if ( cmp( i->Titel, End->Titel ) < 0 )
{
swapString( &i->Titel, &partitionIdx->Titel );
//NOTE: I disabled the following line from the original code, as it was doing nonsense. It was causing a node to point to itself.
//partitionIdx->Prev = partitionIdx;
partitionIdx = partitionIdx->Next;
}
}
swapString( &partitionIdx->Titel, &End->Titel );
return partitionIdx;
}
void Quicksort( TCD *Start, TCD *End, int( *cmp )(const void *, const void *) )
{
//NOTE: In the following if condition, I disabled part of the original code, because a partition of two elements must be sorted
if ( Start != NULL && End != Start /*&& End != Start->Next*/ )
{
TCD *partitionIdx = partition( Start, End, cmp );
if ( Start != partitionIdx )
Quicksort( Start, partitionIdx->Prev, cmp );
if ( partitionIdx != End )
Quicksort( partitionIdx->Next, End, cmp );
}
}
// NOTE:
// The following functions are not part of the algorithm, but are only
// used to test the algorithm.
void AddToList( TCD **head, TCD **tail, char *str )
{
TCD *p;
//allocate new node and fill it with the data
p = malloc( sizeof(*p) );
assert( p != NULL );
p->Titel = str;
p->Next = NULL;
p->Prev = *tail;
//attach new node to list by updating head or next pointer
if ( *head == NULL )
*head = p;
else
(*tail)->Next = p;
//update tail pointer too
*tail = p;
}
void PrintList( FILE *stream, TCD *head )
{
TCD *p;
for ( p = head; p != NULL; p = p->Next )
{
fprintf( stream, "%s\n", p->Titel );
}
fprintf( stream, "\n" );
}
void FreeList( TCD *head )
{
TCD *p = head;
while ( p != NULL )
{
TCD *tmp = p;
p = p->Next;
free( tmp );
}
}
int main( void )
{
TCD *head = NULL, *tail = NULL;
//create linked list with a bit of unsorted test data
AddToList( &head, &tail, "string8" );
AddToList( &head, &tail, "string4" );
AddToList( &head, &tail, "string2" );
AddToList( &head, &tail, "string7" );
AddToList( &head, &tail, "string3" );
AddToList( &head, &tail, "string5" );
AddToList( &head, &tail, "string1" );
AddToList( &head, &tail, "string6" );
//print list before sorting
fprintf( stderr, "List before sort:\n" );
PrintList( stderr, head );
//do the actual sorting
Quicksort( head, tail, strcmp );
//print list after sorting
fprintf( stderr, "List after sort:\n" );
PrintList( stderr, head );
//free the linked list
FreeList( head );
return 0;
}

Printing a string in reverse using a linked list

I build a program that takes a string from a command line and use a linked list to print it in reverse.
I am currently debugging my program and I am just completely stuck. I have a feeling most are memory related.
/*
Takes a string from the command line.
Makes a linked-list out of it in reverse order.
Traverse it to construct a string in reverse.
Clean up (release memory).
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct st_CharNode
{
char theChar;
struct st_CharNode *next;
} CharNode;
void reverseIt( char *stringbuffer );
int main( int argc, char *argv[] )
{
char *stringBuffer;
// Check number of user supplied arguments.
if( argc != 2 )
{
fprintf( stderr, "usage: %s string. This reverses the string "
"given on the command line\n" );
exit( -1 );
}
// Copy the argument so we can make changes to it
stringBuffer = malloc( strlen(argv[1]) );
strcpy( argv[1], stringBuffer );
// Reverse the string
reverseIt( stringBuffer );
// Print the reversed string
printf( "the reversed string is '%s'\n", *stringBuffer );
return 0;
}
// Build a linked list backwards, then traverse it.
void reverseIt( char *stringbuffer )
{
CharNode *head, *node;
char *scan, *stop;
// initialize local vars
head = node = NULL;
// find the start and end of the string so we can walk it
scan = stringbuffer;
stop = stringbuffer + strlen(stringbuffer) + 1;
// walk the string
while (scan < stop)
{
if (head == NULL)
{
head = malloc( sizeof(CharNode*) );
head->theChar = *scan;
head->next = NULL;
}
else
{
node = malloc( sizeof(CharNode*) );
node->theChar = *scan;
node->next = head;
head = node;
}
scan++;
}
// Re-point to the buffer so we can drop the characters
scan = stringbuffer;
// Traverse the nodes and add them to the string
while( head != NULL )
{
*scan = head->theChar;
free( head );
node = head->next;
head = node;
scan++;
}
// Release head
free( head );
}
An error I've seen is that you are using strcpy like this:
strcpy( argv[1], stringBuffer );
which copies the value of stringBuffer into argv[1], you should do it the other way around, like this:
strcpy( stringBuffer, argv[1]);
see the man page of strcpy for more information: http://linux.die.net/man/3/strcpy

working with stack in C

I am trying to implement the following functionality
It takes a line of string as input and then tokenizes them and put them in a stack and later on it prints the buffer reverse
"welcome to the den"
whould show up as
den the to welcome
the problem with what I have so far is that it stops working and after debugging I realize that in the push function called in the main , the value of the token is not getting passed to the function .
Can any one please help me with why it does not pass the string of token to the push function .
I think there is some thing wrong with "char* data;" in the struct
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// self-referential structure
struct stackNode
{
char* data;
struct stackNode *pNext;
};
typedef struct stackNode StackNode;
typedef StackNode *StackNodePtr;
// function prototypes
void push( StackNodePtr *pTop, char value );
//int pop( StackNodePtr *pTop );
//int isEmpty( StackNodePtr pTop );
void printStack( StackNodePtr pCurrent );
int main( void )
{
char *pToken = NULL;
int counter;
char input[BUFFER_SIZE];
StackNodePtr pStack = NULL;
printf("Please enter a line of text here :\n");
gets(input);
pToken = strtok(input, " ");
while(pToken != NULL)
{
push(&pStack, pToken);
printf("%p '%s'\n", pToken, pToken);
pToken = strtok(NULL, " ");
}
printf("I am out of while loop");
printStack(pStack);
return 0;
}
// Insert a node at the stack top
void push( StackNodePtr *pTop, char* value )
{
StackNodePtr pNew;
pNew = malloc( sizeof( StackNode ) );
if ( pNew != NULL )
{
pNew->data = value;
pNew->pNext = *pTop; // insert at top of stack
*pTop = pNew;
}
else
{
printf( "%d not inserted. No memory available.\n", value );
}
}
// output stack contents to the console
void printStack( StackNodePtr pCurrent )
{
if ( pCurrent == NULL )
{
printf( "The stack is empty.\n\n" );
}
else
{
printf( "The stack is:\n" );
while ( pCurrent != NULL )
{
printf( "%s", pCurrent->data );
pCurrent = pCurrent->pNext; // move to next element
}
printf( "NULL\n\n" );
}
}
This works:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 256
// self-referential structure
struct stackNode
{
char* data;
struct stackNode *pNext;
};
typedef struct stackNode StackNode;
typedef StackNode *StackNodePtr;
// function prototypes
void push(StackNodePtr* pTop, char* value);
//int pop( StackNodePtr *pTop );
//int isEmpty( StackNodePtr pTop );
void printStack( StackNodePtr pCurrent );
int main( void )
{
char *pToken = NULL;
int counter;
char input[BUFFER_SIZE];
StackNodePtr pStack = NULL;
printf("Please enter a line of text here :\n");
gets(input);
pToken = strtok(input, " ");
while(pToken != NULL)
{
push(&pStack, pToken);
printf("%p '%s'\n", pToken, pToken);
pToken = strtok(NULL, " ");
}
printf("I am out of while loop");
printStack(pStack);
return 0;
}
// Insert a node at the stack top
void push( StackNodePtr* pTop, char* value )
{
StackNodePtr pNew;
pNew = (StackNode*)malloc( sizeof( StackNode ) );
if ( pNew != NULL )
{
pNew->data = value;
pNew->pNext = *pTop; // insert at top of stack
*pTop = pNew;
}
else
{
printf( "%s not inserted. No memory available.\n", value );
}
}
// output stack contents to the console
void printStack( StackNodePtr pCurrent )
{
if ( pCurrent == NULL )
{
printf( "The stack is empty.\n\n" );
}
else
{
printf( "The stack is:\n" );
while ( pCurrent != NULL )
{
printf( "%s", pCurrent->data );
pCurrent = pCurrent->pNext; // move to next element
}
printf( "NULL\n\n" );
}
}
Input:
welcome to the den
Output:
0x7fff31d13fc0 'welcome'
0x7fff31d13fc8 'to'
0x7fff31d13fcb 'the'
0x7fff31d13fcf 'den'
I am out of while loopThe stack is:
denthetowelcomeNULL
I really only modified the code to compile on a reasonable machine. Just make sure you aren't printing character arrays as decimal types. I didn't fix any other issues, so no guarantees, just that THIS bit compiles and runs. I'm assuming you are going to add onto it later. Just make sure you are tracking your memory allocations.
I suspect the main problem is the mismatch between the prototype for push and it's definition.
void push( StackNodePtr *pTop, char value );
Vs
void push( StackNodePtr *pTop, char *value )
When I tried to compile the original it would not even compile, but maybe some broken would compile it and perhaps ignore the definition.
Here is a working version with the only change being to assign the declaration with the definition. Note that there are a shedload of warnings - you should address these as well.

C linked list searchNode / freeList methods (seg fault)

I've been making the transition from Java to learning C for a class. A current exercise is to implement removeAtFront(), searchNode(), and freeList() methods for a LinkedList. I understand theoretically how this works - I'd have it done quickly in Java, I've just tried for hours and don't understand why the below code doesn't work.
The remove method appears to work, producing the correct modified list, until the search method is called AFTER removing a node. Then a seg fault 11 is always produced. The free method also always produces a seg fault.
I'm not asking for people to do my homework, but if I could be pointed in the right direction that'd be much appreciated!
The given Node* struct is:
typedef struct Node
{
char *word;
struct Node *next;
} Node;
The methods outside of main() read like this:
void insertAtFront( Node **head, char * key )
{
Node *new = malloc( sizeof(Node) );
if (!new) fatal("Malloc of new Node failed");
new->word = key;
new->next = *head;
*head = new;
}
void insertAtTail( Node **head, char * word )
{
if (!(*head)) insertAtFront(head, word);
else insertAtTail(&(*head)->next, word);
}
void removeAtFront( Node ** head )
{
Node *tmp = *head;
if (!tmp) return;
*head = tmp->next;
free(tmp->word);
free (tmp);
}
void removeNode( Node ** head, char * key )
{
Node *tmp = searchNode(*head, key);
if (tmp) removeAtFront (&tmp);
}
Node * searchNode ( Node * head, char * key )
{
if (!head || (strcmp(head->word, key) == 0)) return head;
return searchNode(head->next, key);
}
void freeList( Node ** head )
{
if (!head) return;
if (&(*head)->next) freeList (&(*head)->next);
removeAtFront(head);
}
EDIT: One of the comments fixed my problem with the freeList() method, but others asked for more code. The problem with this assignment is that I am only allowed to modify the insertAtTail(), removeAtFront(), remove(), search(), and freeList() methods. I'll post the main method below though. I think the word values are allocated correctly within that though.
Node *searchNode( Node * head, char * key );
void insertAtFront( Node **head, char * key ); // ALREADY WRITTEN FOR YOU
void insertAtTail( Node **head, char * key );
void removeAtFront( Node ** head );
void removeNode( Node **head, char * key );
void freeList( Node **head );
void printList( Node * head ); // ALREADY WRITTEN FOR YOU
void fatal( char * msg ); // ALREADY WRITTEN FOR YOU
#define BUFFER_CAP 20
int main()
{
Node *head = NULL;
while (1)
{
char option;
printf("\nChoose 'H'ead Insert, 'T'ail insert, 'R'emove, 'S'earch, F'ree, 'Q'uit " );
fflush( stdout );
int result = scanf(" %c%*[^\n]", &option); getchar(); // MAGIC BULLET TO CORRECTLY READ A SINGLE CHAR FROM STDIN
if (result <1) fatal("failure reading from stdin\n");
if (option == 'H' )
{
char * word=malloc(BUFFER_CAP); // DONT ENTER ANY LONG WORDS!
printf("Enter a word to insertAtFront: " );
fflush( stdout );
char * result = fgets( word, BUFFER_CAP, stdin );
if (result==NULL) fatal("failure reading from stdin\n");
strtok(word,"\n"); // overwrites '\n' with '\0'
insertAtFront( &head, word ); /* shallow copy string into list */
printList( head );
}
if (option == 'T' )
{
char * word=malloc(BUFFER_CAP); // DONT ENTER ANY LONG WORDS!
printf("Enter a word to insertAtTail: " );
fflush( stdout );
char * result = fgets( word, BUFFER_CAP, stdin );
if (result==NULL) fatal("failure reading from stdin\n");
strtok(word,"\n"); // overwrites '\n' with '\0'
insertAtTail( &head, word ); /* shallow copy string into list */
printList( head );
}
if (option == 'R' )
{
char * word=malloc(BUFFER_CAP); // DONT ENTER ANY LONG WORDS!
printf("Enter a word to remove: " );
fflush( stdout );
char * result = fgets( word, BUFFER_CAP, stdin );
if (result==NULL) fatal("failure reading from stdin\n");
strtok(word,"\n"); // overwrites '\n' with '\0'
removeNode( &head, word );
printList( head );
free( word ); // we were just using it for matching
}
if (option == 'S' )
{
char * word=malloc(BUFFER_CAP); // DONT ENTER ANY LONG WORDS!
printf("Enter a word to find: " );
fflush( stdout );
char * result = fgets( word, BUFFER_CAP, stdin );
if (result==NULL) fatal("failure reading from stdin\n");
strtok(word,"\n"); // overwrites '\n' with '\0'
if (searchNode( head, word ))
fprintf(stderr, "%s FOUND\n",word );
else
fprintf(stderr, "%s NOT FOUND\n",word );
printList( head );
free( word ); // we were just using it for matching
}
if (option == 'F' ) // free the entire list (remember to set head to NULL)
{
freeList( &head );
printList( head );
}
else if (option == 'Q' )
exit( 0 );
} // END WHILE
return 0;
}
when you are allocating memory for node using Node *new = malloc( sizeof(Node) ); , you are allocating memory for pointer but not for data. your are do allocate memory for char also like: (its just an idea)
new->word= malloc(sizeof(char)*(strlen(key) + 1));
strcpy(new->word, key)
Other wise you have to be use that you allocate memory for key dynamically. (because you do free(tmp->word);)
I think you should put some more code. How do you pass key?
Yeah as indicated in earlier answer, you are not allocating memory for word in each node though you have allocated the memory for the node. Sometimes it might work, without causing segfault, at that time you are using "somebody's" memory, that you haven't claimed, causing to corrupt that memory locations.

Resources