C linked list searchNode / freeList methods (seg fault) - c

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.

Related

Alphabetize and word frequency from a file using strtok in C

My goal is to analyze a text file, tokenize each word, then alphabetize each word with its word frequency.
Example:
Input: The house is on the ground on earth.
Output:
earth - 1
ground - 1
house - 1
is - 1
on - 2
the - 2
I have been able to open the file, read the file line by line, tokenize each word, converted the tokens to lowercase. I am stuck grouping and alphabetizing each token.
#include <stdio.h>
#include <stdlib.h>
void lower_string(char s[]);
int main()
{
FILE *file;
//char path[100];
char ch[100];
int characters;
/* Input path of files to merge to third file
printf("Enter source file path: ");
scanf("%s", path);
file = fopen(path, "r");*/
file = fopen("test.txt", "r"); //testing w.o repeated input
/* Check if file opened successfully */
if (file == NULL)
{
printf("\nUnable to open file.\n");
printf("Please check if file exists and you have read privilege.\n");
exit(EXIT_FAILURE);
}
const char delim[] = " ,.;!?[\n]";
char *token;
int tokenNum;
while (fgets(ch, sizeof(ch), file) != NULL)
{
lower_string(ch);
token = strtok(ch, delim);
while (token != NULL)
{
printf("Token:%s\n", token);
token = strtok(NULL, delim);
tokenNum++;
}
}
printf("%d\n", tokenNum); //total words testing
/* Close files to release resources */
fclose(file);
return 0;
}
void lower_string(char s[])
{
int c = 0;
while (s[c] != '\0')
{
if (s[c] >= 'A' && s[c] <= 'Z')
{
s[c] = s[c] + 32;
}
c++;
}
}
I have been looking into building and manipulating an ordered linked list of integers and binary search tree of integers. I'm having a hard time figuring out where I should begin to implement these features. So far i have been looking at the code below for ordered linked list.
#include <stdio.h>
#include <stdlib.h>
//These structures are declared globally so they are available to all functions
//in the program.
typedef struct list_node_s
{ //defines structure of one node
int key; //key value - here an integer
int count; //frequency key value encountered in input
struct list_node_s *restp; //pointer to the next node in list = NULL if EOL
} list_node_t;
typedef struct //defines head of list structure
{
list_node_t *headp; //pointer to first node in list, NULL if list is empty
int size; //current number of nodes in the list
} ordered_list_t;
//Prototypes
list_node_t * insert_in_order (list_node_t * old_listp, int new_key);
void insert (ordered_list_t * listp, int key);
int delete (ordered_list_t * listp, int target);
list_node_t * delete_ordered_node (list_node_t * listp, int target,int *is_deleted);
void print_list (ordered_list_t * listp);
#define SEND -999 //end of input sentinal
int main (void)
{
int next_key;
ordered_list_t my_list = {NULL, 0};
printf("\n\nProgram to build, display and manipulate (delete) an Ordered Linked List \n");
printf("\nAdapted from code in \"Problem Solving and Programming in C\" by J.R. Hanly and E.B. Koffman\n\n");
printf ("enter integer keys - end list with %d\n", SEND);
/* build list by in-order insertions*/
for (scanf ("%d", &next_key);
next_key != SEND;
scanf ("%d", &next_key))
{
insert (&my_list, next_key);
}
/* Display completed list */
printf ("\nOrdered list as built:\n");
print_list(&my_list);
/* Process requested deletions */
printf("enter key value for node to be removed from list or %d to end > ", SEND);
for (scanf ("%d", &next_key);
next_key != SEND;
scanf ("%d", &next_key))
{
if (delete (&my_list, next_key))
{
printf ("%d deleted.\n New list:\n", next_key);
print_list (&my_list);
}
else
{
printf ("No deletion. %d not found\n", next_key);
}
printf ("enter key value for node to be removed from list or %d to end > ", SEND);
}
return (0);
}
/* prints contents of a linked list Display the elements in the list pointed to by the pointer list.*/
void print_list (ordered_list_t * listp)
{
list_node_t * tmp;
for (tmp = listp->headp; tmp != NULL; tmp = tmp->restp)
printf ("key = %d; count = %d\n", tmp->key, tmp->count);
printf ("\n\n");
}
//Inserts a new node containing new_key into an existing list and returns a pointer to the first node of the new list
list_node_t * insert_in_order (list_node_t * old_listp, int new_key)
{
list_node_t * new_listp;
if (old_listp == NULL) //check for end of list (EOL)
{
new_listp = (list_node_t *) malloc (sizeof (list_node_t));
new_listp->key = new_key;
new_listp->count = 1;
new_listp->restp = NULL;
}
else if (old_listp->key == new_key) //check for matching key, increment count
{
old_listp->count++;
new_listp = old_listp;
}
else if (old_listp->key > new_key) //Next node key value > new key, so insert new node at current location
{
new_listp = (list_node_t *) malloc (sizeof (list_node_t));
new_listp->key = new_key;
new_listp->count = 1;
new_listp->restp = old_listp;
}
else
{
new_listp = old_listp;
new_listp->restp = insert_in_order (old_listp->restp, new_key);
}
return (new_listp);
}
//inserts a node into an ordered list_node_t
void insert (ordered_list_t * listp, int key)
{
++(listp->size);
listp->headp = insert_in_order (listp->headp, key);
}
//deletes the first node containing the target key from an ordered list; returns 1
//if target found & deleted, 0 otherwise (means target not in list)
int delete (ordered_list_t * listp, int target)
{
int is_deleted;
listp->headp = delete_ordered_node (listp->headp, target, &is_deleted);
if (is_deleted)
--(listp->size); //reduce current node count (size); keep size of list current
return (is_deleted);
}
/* deletes node containing target key from a list whose head is listp; returns a pointer
to the modified list (incase it is the first node, pointed to by listp), frees
the memory used by tyhe deleted node and sets a flag to indicate success (1) or
failure (0; usually means no such node found).
*/
list_node_t * delete_ordered_node (list_node_t * listp, int target, int *is_deleted)
{
list_node_t *to_freep, *ansp;
// if list empty, nothing to do; return NULL
printf ("check for empty list; target: %d \n", target);
if (listp == NULL)
{
*is_deleted = 0;
ansp = NULL;
}
//if first node is to be deleted, do it; relink rest of list to list header struct
else if (listp->key == target)
{
printf ("at first node; target: %d \n", target);
*is_deleted = 1;
to_freep = listp; //keeps track of node memory location to be freed
ansp = listp->restp;
free (to_freep); //release the memory of the deleted node for reuse
}
//if target exists, it is further down the list (recursive step), make recursive call
//to move down the list looking for the target value
else
{
printf ("chase down list to find: %d \n", target);
ansp = listp;
ansp->restp = delete_ordered_node (listp->restp, target, is_deleted);
}
return (ansp);
}
I'm finding it hard to implement that with strtok.
12/4 EDIT:
added: Nodes for BST.
Questions-
Don't know if key needs to be tracked.(I assume it'll be useful to pull specific words).
Where/how would I add the logic to alphabetize the tree.(study sources appreciated)
How do I pass each word through this tree?
#define WLENGTH 100
//Base Node info
struct node
{
char word[WLENGTH];
int key;
int freq;
struct node *left, *right;
};
//Function to create a new node
struct node *newNode(char wordn, int item, int freqn)
{
struct node *temp = (struct node *) malloc(sizeof(struct node));
temp->word = wordn;
temp->key = item;
temp->freq = freqn;
temp->left = temp->right = NULL;
return temp;
}
//Function to place nodes in order
void inorder(struct node *root)
{
if (root != NULL)
{
inorder(root->left);
printf("%d ", root->key);
inorder(root->right);
}
}
/*Function to insert a new node with given key*/
struct node* insert(struct node* node, int key)
{
/* If the tree is empty, return a new node */
if (node == NULL)
return newNode(key);
/* Otherwise, recur down the tree */
if (key < node->key)
node->left = insert(node->left, key);
else if (key > node->key)
node->right = insert(node->right, key);
/* return the (unchanged) node pointer */
return node;
}
At the request of the OP, here is a bit of code to bulk load an entire text file for processing:
FILE *mustOpen( char *fname, char *mode ) {
FILE *fp = fopen( fname, mode );
if( fp == NULL ) {
fprintf( stderr, "Cannot open '%s'\n", fname );
exit( EXIT_FAILURE );
}
return fp;
}
// Passed the path to a file, opens, measures and bulk loads entire file (plus terminating '\0')
char *loadFile( char *fname ) {
FILE *fp = mustOpen( fname, "rb" );
fseek( fp, 0, SEEK_END );
size_t size = ftell( fp );
fseek( fp, 0, SEEK_SET );
char *buf;
if( ( buf = malloc( size + 1) ) == NULL )
fprintf( stderr, "Malloc() failed\n" ), exit( EXIT_FAILURE );
if( fread( buf, sizeof *buf, size, fp ) != size )
fprintf( stderr, "Read incomplete\n" ), exit( EXIT_FAILURE );
fclose( fp );
*(buf + size) = '\0'; // xtra byte allows strXXX() to work
return buf; // pointer to heap allocated buffer containing file's bytes
}
Remember to free() the buffer when done with its contents.
With the entire text loaded (and NULL terminated), here is a way to skip along the entire "string" finding each "word" (as defined by the delimiters):
for( char *cp = buf; (cp = strtok( cp, delim )) != NULL; cp = NULL ) {
/* process each single "word" */
}
Since the "text" is in memory, instances of each of the "words" are in memory, too. All that's needed is populating a BST with nodes that 'point to' one instance of each "word" and a counter that counts multiple occurrences of each word.
Finally, an "in order" traversal of the BST will give an alphabetised list of words and their frequency in the text.
Be sure to compartmentalize each of the functions. The "blocks" of functionality can then be re-used in other projects, and, who knows?... You may want to first load a dictionary and only report the words (and locations) that do not appear in the dictionary (typos?). The code that handles the BST "structure" (searching, adding, traversing) should be somewhat independent of what "information fields" comprise each node.

How to make a singly linked list based on inputs

I'm just learning linked lists, and want to make one with inputs from the console, but somehow it's not working.
#include <stdio.h>
#include <stdlib.h>
typedef struct datatype Node;
struct datatype {
int data;
Node *next;
};
int main() {
int i,n;
Node *node[50];
printf("Number of intergers: ");
scanf("%d",&n);
for(i=0; i<n; i++) {
printf("Enter interger one by one: ");
scanf("%d",&node[i]->data);
node[i]->next=node[i+1];
}
for(i=0;i<n;i++)
printf("%d->",node[i]->data);
}
Building on Andreas's comment, here is a commented and memory safe implementation. You don't necessarily have to use malloc and free; you could instead have a fixed memory pool:
#define MAX_NODE_SIZE 256
...
Node node[MAX_NODE_SIZE];
followed by a check to make sure the user never puts n greater than MAX_NODE_SIZE. However if you tried to implement list inserts and deletes things would get complicated. Also, using an index "i" to iterate through a linked list kind of defeats the point of having a linked list.
Here is the malloc/free version:
#include <stdio.h>
#include <stdlib.h>
typedef struct datatype Node;
struct datatype {
int data;
Node *next;
};
int main() {
//Allocate the root node
Node *rootnode=malloc(sizeof(Node));
rootnode->data=0; rootnode->next=NULL;
Node *currentnode=rootnode;
//Populate n and check for validity
printf("Number of integers: ");
int n;
scanf("%d",&n);
if(n<=0){
printf("List must have a nonzero/nonnegative number of elements.\n");
return 1;
}
//Populate the list
for(int i=0; i<n; i++) {
printf("Enter integer one by one: ");
scanf("%d",&currentnode->data);
//If there is more data...
if(i!=n-1){
//Allocate room for the data
currentnode->next=malloc(sizeof(Node));
//Initialize things correctly
currentnode->next->data=0;
currentnode->next->next=NULL;
//Step to the next node.
currentnode=currentnode->next;
}
}
//Print the list
currentnode=rootnode;
do {
printf("%d",currentnode->data);
if(currentnode->next!=NULL)
printf(" -> ");
else
printf("\n");
} while((currentnode=currentnode->next)!=NULL);
//Deallocate the list
currentnode=rootnode;
while(currentnode!=NULL){
Node *next=currentnode->next;
free(currentnode);
currentnode=next;
}
return 0;
}
Example output:
user#desktop:~$ ./a.out
Number of integers: 3
Enter integer one by one: 1
Enter integer one by one: 2
Enter integer one by one: 3
1 -> 2 -> 3
You must allocate memory for the individual nodes of the linked list. Instead, you are allocating memory for 50 pointers to individual nodes, but not for the nodes themselves.
I suggest you create a standard linked list and use the function malloc for the individual nodes, like this:
typedef struct Node {
int data;
struct Node *next;
} Node;
int main( void )
{
//this pointer always points to the first element, or NULL if there is no first element
Node *pRoot = NULL;
//this pointer always points to the NULL pointer at the end of the list, which is, when the list is empty, the root pointer
Node **ppNext = &pRoot;
Node *pCurrent;
int retval, n;
//ask user for total number of data elements
printf( "Number of integers: " );
retval = scanf( "%d", &n );
if ( retval != 1)
{
fprintf( stderr, "scanf failed!\n" );
goto cleanup;
}
//build the list from user input
for ( int i = 0; i < n; i++ )
{
//allocate memory for new node
pCurrent = malloc( sizeof( Node ) );
if ( pCurrent == NULL )
{
fprintf( stderr, "malloc failed!\n" );
goto cleanup;
}
//ask user for individual data elements
printf( "Enter integer one by one: " );
retval = scanf( "%d", &pCurrent->data );
if ( retval != 1 )
{
fprintf( stderr, "scanf failed!\n" );
free( pCurrent );
goto cleanup;
}
pCurrent->next = NULL;
//link new node to linked list and update ppNext
*ppNext = pCurrent;
ppNext = &pCurrent->next;
}
//print the list
for ( pCurrent = pRoot; pCurrent != NULL; pCurrent = pCurrent->next )
{
printf( "%d\n", pCurrent->data );
}
cleanup:
//free the linked list
for ( pCurrent = pRoot; pCurrent != NULL; )
{
Node *tmp = pCurrent;
pCurrent = pCurrent->next;
free( tmp );
}
return 0;
}
Please note that in my code, before I use the value written to by scanf, I check the return value of scanf. This is necessary because the function may fail and not write any value, for example when the user enters letters instead of numbers. See this page for further information:
A beginner's guide away from scanf()

Why I get this wrong output?

I created this simple double linked list.
The problem is that when I print all its elements, they have the same char value even if the variable "a" changes every time.
typedef struct node{
char *name;
struct node *n;
struct node *p;
} N;
N *h=NULL;//head
void insert(char *value){
N *temp=malloc(sizeof(N));
if(h==NULL){
h=temp;
temp->name=strdup(value);
}
else{
N *curr=h;
while(curr->n!=NULL)
curr=curr->n;
curr->n=temp;
temp->p=curr;
temp->name=strdup(value);
}
}
void print(){
N *temp=h;
printf("%s\n", temp->name);
while(temp->n!=NULL){
printf("%s\n", temp->name);
temp=temp->n;
}
}
int main(){
char a[...];
fgets(a,...)
//there is a while section: every time i enter in it, there is:
char *input=a;
insert(input);
print();
}
So I expected something as:
Lion
Bear
Goat
....
Instead i get:
Lion, then
Bear
Bear, then
Goat
Goat
Goat
etc...
There are a couple of issues. First you have a bug in print() that prevents the last value from being displayed. Check temp instead of temp->n:
void print()
{
N *temp=h;
while(temp !=NULL){
printf("%s\n", temp->name);
temp=temp->n;
}
}
Your extra printf() call (before the while loop) is why the first value was printed twice.
Also you must assign p and n when you add a new node. You can't assume that they will be NULL if you don't assign them.
void insert(char *value)
{
N *temp=malloc(sizeof(N));
if(h==NULL){
h=temp;
temp->p = NULL;
temp->n = NULL;
temp->name=strdup(value);
}
else{
N *curr=h;
while(curr->n!=NULL)
curr=curr->n;
curr->n=temp;
temp->p=curr;
temp->n = NULL;
temp->name=strdup(value);
}
}
Also, do you need the list to be double-linked? You never use the p pointer.
You're pointing to the same memory for each list element. This code
temp->name=value;
merely copies the value of the pointer to temp->name because of the definition of the structure:
typedef struct node{
char *name;
struct node *n;
struct node *p;
} N;
name is just a pointer. You need to duplicate the string that value points to, not just point name at value (input validation and error checking are left as an exercise for the reader...):
char *duplicateString( const char *inputString )
{
char newString = malloc( strlen( inputString ) + 1 );
strcpy( newString, inputString );
return( newString );
}
so
temp->name = duplicateString( value );
Just remember to call free( temp->name ) before you call free( temp ) to free your node.
or just use strdup() if you're on a POSIX system:
temp->name = strdup( value );

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

hard linked list in 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
}

Resources