In a struct Node type, there is a char* data. And each node uses a linked list to combine.
If the type of "data" is INT it is ok. (ex. the age: 23,45,33....) But when the type turn to "char * ", ex. save name: “Jack”,"Jay","Jame". The value is all the same, the later will cover the fronter. ex:
First time input: Jack. The list is Jack
Second time input: Jay. The list is Jay Jay
Third time input:Jame. The list is Jame Jame Jame
The code is like:
#include <stdio.h>
#include <stdlib.h>
typedef struct listNode{
char *data;
struct listNode *next;
} *ListNodePtr;
typedef struct list {
ListNodePtr head;
} List;
List new_list(){
List temp;
temp.head = NULL;
return temp;
}
//Student Courses
void insert_at_front(ListNodePtr* head, char *data){
ListNodePtr new_node = malloc(sizeof(struct listNode));
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
void print_list(List *self)
{
ListNodePtr current = self->head;
while(current!=NULL)
{
printf("%s \n", current->data);
current = current->next;
}
printf("\n");
}
int main()
{
char i = 'y';
char *name;
List mylist = new_list();
while(i=='y'){
printf("your name is :");
scanf("%s",name);
insert_at_front(&mylist.head,name);
print_list(&mylist);
}
return 0;
}
The good news is your thinking on list operation is not too far off... the bad news is it isn't right-on either. The biggest stumbling blocks you have are just handling the basics of user-input, and insuring your have storage for each bit of data you want to store.
In main() when you declare char *name;, name is an uninitialized pointer. It does not point to any valid memory yet in which you can store the characters that make up name (plus the terminating nul-character). The only thing you can store in a pointer, is a memory address -- and, to be useful, that memory address must be the start of a valid block of memory sufficiently sized to hold what it is you are attempting to store.
In your code, since name does not point to any valid block of memory capable of storing the characters in name, you immediately invoke Undefined Behavior with scanf("%s",name); (Boom! - "Game Over" for your program).
Since you are reading a name, rarely over 64 characters, just use a fixed-size buffer to hold name to pass it to insert_at_front. Don't skimp on buffer size, so just to be sure, you can use something reasonable like 512 bytes. Don't use magic numbers in your code, so if you need a constant:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 512 /* if you need a constant, #define one (or more) */
...
int main (void) {
char i = 'y';
char name[MAXN] = ""; /* fixed size buf for reading name input */
...
Now for your structs, you are using a "wrapper" struct holding head that essentially wraps your list. It is fine to do, though not required. However, since you are using one, you need to either declare it using automatic storage (and pass it as a parameter) or dynamically allocate for it. Since you use a new_list function, if you were using automatic storage, you would have to declare mylist as type List in main() and pass it as a parameter to new_list() for initialization.
Why? You cannot return temp from new_list because temp was declared within new_list (it is local to new_list) and the function stack for new_list (the memory that contains the temp variable) is destroyed (released for reuse) when new_list returns.
If you want to return a pointer from new_list, then you must either (1) pass a previously declared temp from main() to new_list -- or -- (2) dynamically allocate for temp in new_list giving temp allocated storage duration so that the memory containing temp survives until free() is called on that memory, or the program ends. The normal option is (2), though there is nothing wrong with (1) if you adequately account for the storage duration of the variable.
Makes a few tweaks, and avoiding typedeffing a pointer, because It is NOT a good idea to typedef pointers?, and adding a convenient example of the flexibility you have to track list statistics in your wrapper struct, you could do something similar to:
typedef struct lnode { /* don't typedef pointers -- it will confuse you */
char *data;
struct lnode *next;
} lnode;
typedef struct list { /* you pass this list to insert_at_front */
lnode *head;
size_t size; /* you can track any list stats you like */
} list;
/* create a dynamically allocated list struct */
list *new_list (void)
{
list *temp = malloc (sizeof *temp); /* create storage for list */
if (!temp) { /* validate ALL allocations */
perror ("malloc-new_list");
return NULL;
}
temp->head = NULL; /* initialize head NULL */
temp->size = 0;
return temp; /* return pointer to new list */
}
To help make lists more logical while you are learning, it often helps to create a separate function that is responsible for creating each node you add to your list. This allows you to concentrate on which items within your node struct need storage allocated, and provides a convenient place to handle the allocation (and validation of that allocation), as well as initializing all values in a single place -- without cluttering your list logic. You could implement a create_node function similar to:
/* create new dynamically allocated node, initialize all values */
lnode *create_new_node (const char *data)
{
lnode *new_node = NULL;
if (!data) { /* validate data not NULL */
fputs ("error: data is NULL in create_new_node.\n", stderr);
return NULL;
}
new_node = malloc (sizeof *new_node); /* allocate/validate node */
if (!new_node) {
perror ("malloc-new_node");
return NULL;
}
/* allocate/validate storage for data */
if (!(new_node->data = malloc (strlen (data) + 1))) {
perror ("malloc-new_node->data");
free (new_node);
return NULL;
}
strcpy (new_node->data, data); /* copy data to new_node->data */
new_node->next = NULL; /* set next pointer NULL */
return new_node; /* return pointer to new_node */
}
(note: you have allocated for (1) the list, (2) the node, and (3) the data)
That leaves your logic for insert_at_front clean and readable. Additionally, you need to always use a proper type for any list operation, especially where any allocation is involved, that allows you to gauge success/failure of the list operation. Generally returning a pointer to the node added (a new head here) or NULL on failure, is all you need, e.g..
/* insert new node at front, returning pointer to head
* to guage success/failure of addition.
*/
lnode *insert_at_front (list *mylist, const char *data)
{
lnode *new_node = create_new_node(data);
if (!new_node)
return NULL;
new_node->next = mylist->head;
mylist->head = new_node;
mylist->size++;
return mylist->head;
}
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. Get in the habit now to cleaning up after yourself -- it will pay big dividends as your programs become more complex. If you are dealing with a list, and nodes, then write a function to free all data, nodes and the list when you are done with it. Something simple is all that is needed, e.g.
/* you are responsible for freeing any memory you allocate */
void free_list (list *mylist)
{
lnode *current = mylist->head;
while (current) {
lnode *victim = current;
current = current->next;
free (victim->data);
free (victim);
}
free (mylist);
}
With user input, your are responsible for validating that you received good input and that it satisfies any conditions you have placed on the input. You are also responsible for insuring that the state of the input buffer is ready for the next input operation. That means clearing any extraneous characters that may be left in the input buffer (e.g. stdin) that would cause your next attempt at input to fail. A simple helper function to empty stdin can save you from a world of trouble.
/* you are responsible for the state of stdin when doing user input */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
Further, every input function you will use has a return. You must validate the return of any input function you use to determine if (1) valid input was read, (2) whether the user canceled input by generating a manual EOF, and (3) when using scanf whether a matching or input failure occurred. So always check the return! For example:
while (i == 'y' || i == '\n') { /* 'y' or (default '\n') */
fputs ("\nenter name: ", stdout);
if (scanf ("%511[^\n]", name) != 1) { /* ALWAYS CHECK RETURN! */
fputs ("error: invalid input or user canceled.", stderr);
return 1;
}
empty_stdin(); /* empty any chars that remain in stdin */
...
Putting it altogether in a short example, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 512 /* if you need a constant, #define one (or more) */
typedef struct lnode { /* don't typedef pointers -- it will confuse you */
char *data;
struct lnode *next;
} lnode;
typedef struct list { /* you pass this list to insert_at_front */
lnode *head;
size_t size; /* you can track any list stats you like */
} list;
/* create a dynamically allocated list struct */
list *new_list (void)
{
list *temp = malloc (sizeof *temp); /* create storage for list */
if (!temp) { /* validate ALL allocations */
perror ("malloc-new_list");
return NULL;
}
temp->head = NULL; /* initialize head NULL */
temp->size = 0;
return temp; /* return pointer to new list */
}
/* create new dynamically allocated node, initialize all values */
lnode *create_new_node (const char *data)
{
lnode *new_node = NULL;
if (!data) { /* validate data not NULL */
fputs ("error: data is NULL in create_new_node.\n", stderr);
return NULL;
}
new_node = malloc (sizeof *new_node); /* allocate/validate node */
if (!new_node) {
perror ("malloc-new_node");
return NULL;
}
/* allocate/validate storage for data */
if (!(new_node->data = malloc (strlen (data) + 1))) {
perror ("malloc-new_node->data");
free (new_node);
return NULL;
}
strcpy (new_node->data, data); /* copy data to new_node->data */
new_node->next = NULL; /* set next pointer NULL */
return new_node; /* return pointer to new_node */
}
/* insert new node at front, returning pointer to head
* to guage success/failure of addition.
*/
lnode *insert_at_front (list *mylist, const char *data)
{
lnode *new_node = create_new_node(data);
if (!new_node)
return NULL;
new_node->next = mylist->head;
mylist->head = new_node;
mylist->size++;
return mylist->head;
}
/* print_list - tweaked for formatted output */
void print_list (list *self)
{
lnode *current = self->head;
while (current != NULL)
{
if (current == self->head)
printf (" %s", current->data);
else
printf (", %s", current->data);
current = current->next;
}
putchar ('\n');
}
/* you are responsible for freeing any memory you allocate */
void free_list (list *mylist)
{
lnode *current = mylist->head;
while (current) {
lnode *victim = current;
current = current->next;
free (victim->data);
free (victim);
}
free (mylist);
}
/* you are responsible for the state of stdin when doing user input */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
char i = 'y';
char name[MAXN] = ""; /* fixed size buf for reading name input */
list *mylist = new_list();
while (i == 'y' || i == '\n') { /* 'y' or (default '\n') */
fputs ("\nenter name: ", stdout);
if (scanf ("%511[^\n]", name) != 1) { /* ALWAYS CHECK RETURN! */
fputs ("error: invalid input or user canceled.", stderr);
return 1;
}
empty_stdin(); /* empty any chars that remain in stdin */
insert_at_front (mylist, name); /* insert name */
fputs ("continue (y)/n: ", stdout); /* prompt to continue */
scanf ("%c", &i); /* read answer (or '\n' from pressing Enter) */
}
printf ("\nfinal list (%zu nodes):", mylist->size);
print_list (mylist);
free_list (mylist); /* don't forget to free memory you allocate */
return 0;
}
(note: the prompt to continue allows the user to simply press Enter to indicate he wants to continue entering names, any other character will exit. That is why (y) is shown as the default in the prompt -- can you explain why and how that works?)
Example Use/Output
$ ./bin/llinshead
enter name: Mickey Mouse
continue (y)/n:
enter name: Donald Duck
continue (y)/n:
enter name: Pluto (the dog)
continue (y)/n:
enter name: Minnie Mouse
continue (y)/n: n
final list (4 nodes): Minnie Mouse, Pluto (the dog), Donald Duck, Mickey Mouse
Memory Use/Error Check
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/llinshead
==5635== Memcheck, a memory error detector
==5635== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==5635== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==5635== Command: ./bin/llinshead
==5635==
enter name: Mickey Mouse
continue (y)/n:
enter name: Donald Duck
continue (y)/n:
enter name: Pluto (the dog)
continue (y)/n:
enter name: Minnie Mouse
continue (y)/n: n
final list (4 nodes): Minnie Mouse, Pluto (the dog), Donald Duck, Mickey Mouse
==5635==
==5635== HEAP SUMMARY:
==5635== in use at exit: 0 bytes in 0 blocks
==5635== total heap usage: 9 allocs, 9 frees, 134 bytes allocated
==5635==
==5635== All heap blocks were freed -- no leaks are possible
==5635==
==5635== For counts of detected and suppressed errors, rerun with: -v
==5635== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have any further questions.
Related
In the example below, I created a linked list and I can add numbers successfully. However, at the
end of the execution, the function named "traverse" does not work. How can I fix this error?
Here is my code:
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
struct node
{
int data;
struct node*prev;
struct node*next;
};
void add( node*head,int number )
{
node*ptr = NULL;
if( head == NULL )
{
head = (node*)malloc(sizeof(node));
head->data = number;
head->next = NULL;
head->prev = NULL;
ptr = head;
}
else
{
ptr->next = (node*)malloc(sizeof(node));
ptr->next->prev = ptr;
ptr = ptr->next;
ptr->data = number;
ptr->next = NULL;
}
}
void traverse( node* head )
{
while( head != NULL )
{
printf("%d ",head->data);
head = head->next;
}
}
int main( void )
{
node *head = NULL;
int number;
char response;
printf("%s\n","Do you want to enter a number in linked list(y/n)?" );
scanf("%c",&response);
while( response == 'y' || response == 'Y' )
{
printf("\nEnter num..> ");
scanf("%d",&number);
add(head,number);
printf("%s\n","Do you want to continue(y/n)?" );
response = getche();
}
printf("\nYour doubly linked list\n");
traverse(head);
getch();
return 0;
}
when "traverse" is called, the console print space like the following image.
If you have decided on C, then continuing from the comments, you are attempting to update a local copy of the pointer head in add(). As mentioned, you have two option, either change the return type of add() to node *add() so you can return ptr and assign as the new head back in main(), or pass the address of head as the first parameter and update the node stored at the original pointer address in add().
You can pass the address of head to add() as follows:
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
(note: since you pass the address of head as a parameter, you must remove one level of indirection from the pointer-to-pointer in add() by dereferncing head (e.g. *head) in order to update the node at the original pointer address. You also need to use the (*head) when further derferencing the pointer with -> due to C operator precedence -- so you get the original pointer address before -> is applied)
Note, the add() function uses a method call Forward-Chaining to add each node to the list in O(1) time. This also means the list will hold the numbers in the reverse order they were entered (last first). You have two options to insert in-order, (1) iterate to the end of the list each time and add a new end node (highly inefficient for large lists, no longer O(1) time, or (2) use another tail pointer that always points to the last node to allow in-order insertions in O(1) time.
You would then call your add() function in main() with
add (&head, number);
Do NOT make things difficult on yourself when testing your list implementation. There is no reason to have to type 'y' then a number and 'y' again before every number you add to your list (that would drive me nuts...). Just add numbers to your list with a loop, you can do input later, e.g.
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
(note: conio.h has been removed and getchar() used to hold the terminal open on windows. Since I'm on Linux, the final getchar() is not compiled as part of my executable)
Your traverse() function will work, but get in the habit of using a separate separate pointer to iterate over you list. This isn't always required, and isn't needed in traverse() since you can use the local copy of head, but always using a temporary pointer to iterate with leave you with the original head address if you need it for use later in your function, e.g.
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
Notice also the delete_list() function added to free() all memory added for your list. You won't always be declaring lists in main() where the memory is freed on exit. Get in the habit of keeping track of the memory you allocate and freeing the memory before your pointer goes out of scope (otherwise, you will create a memory leak)
The full program would be:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *prev, *next;
} node;
void add (node **head, int number)
{
node *ptr = malloc (sizeof *ptr);
if (!ptr)
return;
ptr->data = number; /* initialized new node data */
ptr->prev = ptr->next = NULL; /* initialized both pointers NULL */
if ( *head != NULL ) { /* if not 1st node */
(*head)->prev = ptr; /* Forward-Chain new node */
ptr->next = *head;
}
*head = ptr; /* set head = new node */
}
void traverse (const node *head)
{
const node *iter = head; /* optional, but good practice */
while (iter) {
printf ("%d ", iter->data);
iter = iter->next;
}
putchar ('\n');
}
void delete_list (node *head)
{
node *iter = head;
while (iter) {
node *victim = iter;
iter = iter->next;
free (victim);
}
}
int main (void)
{
node *head = NULL; /* list pointer initialized NULL */
for (int i = 0; i < 20; i++) /* just add 20 nodes to list */
add (&head, i + 1);
traverse (head);
delete_list (head);
head = NULL;
/* hold terminal open on windows only */
#if defined (_WIN32) || defined (_WIN64)
getchar();
#endif
}
Example Use/Output
$ ./bin/llmess
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/llmess
==16661== Memcheck, a memory error detector
==16661== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16661== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==16661== Command: ./bin/llmess
==16661==
20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
==16661==
==16661== HEAP SUMMARY:
==16661== in use at exit: 0 bytes in 0 blocks
==16661== total heap usage: 21 allocs, 21 frees, 1,504 bytes allocated
==16661==
==16661== All heap blocks were freed -- no leaks are possible
==16661==
==16661== For counts of detected and suppressed errors, rerun with: -v
==16661== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I have the following program which reads words from a text file and creates a single linked list, with each node containing: word, count, next.
When a word already exists in the linked list the count is updated, otherwise, a node is created at the end of the linked list.
All of my functions work, except for the one where I am adding a word to the end of the linked list. There is likely an error with linkage of the nodes?
with my following text file: line1 "my name is natalie", line 2 "my dog is niko"
I should be getting the following output: my(2), name(1), is(2), natalie(1), dog(1), niko(1)
but I am getting: my(2), dog(2), s(1), iko(1), is(1), niko(1)
WHERE IS MY ERROR?
//function to add word to linked list
struct WordNode *addWord(char* word, struct WordNode *wordListHead){
//create new node
struct WordNode *current = malloc(sizeof(struct WordNode));
current->word = word;
current->count = 1;
current->next = NULL;
//
while(1){
if((wordListHead != NULL)&&(wordListHead->next ==NULL)){
//connect
wordListHead->next=current;
break;
}
wordListHead = wordListHead->next;
}
}
called here in main:
char *filename = argv[1];
FILE *fp = fopen(filename, "r");
printf("%s\n", filename);
if (fp == NULL){
printf("Error: unable to open the file ");
printf("%s", filename);
return 1;
}
else {
char *delim = " \t\n,:;'\".?!#$-><(){}[]|\\/*&^%#!~+=_"; // These are our word delimiters.
char line[1000];
char * token;
int count = 0;//count used so that first word of the doc can be created as head node
//create head pointer
struct WordNode *wordListHead = NULL;
//create current pointer
struct WordNode *current = NULL;
//iterate through each line in file
while(fgets(line, 1000, fp)){
//seperate each word
//first word of line
token = strtok(line, delim);
printf("%s\n", token);
if(count == 0){
//first word of document, create first wordnode (head node)
wordListHead = malloc(sizeof(struct WordNode));
wordListHead->word = token;
wordListHead->count = 1;
wordListHead->next = NULL;
}
else{
//check if first word of line exists in linked list
if((doesWordExist(token, wordListHead)) == 0){
//update count
updateCount(token, wordListHead);
}
else{
//create node
addWord(token, wordListHead);
}
}
//iterate through all the other words of line
while ((token=strtok(NULL, delim)) != NULL){
printf("%s\n", token);
//check if name is in linked list
if((doesWordExist(token, wordListHead)) == 0){
//update count
updateCount(token, wordListHead);
}
else{
//create node
addWord(token, wordListHead);
}
}
count++;
}
printWordList(wordListHead);
}
}
struct defined here:
//structure definition of linked list node
#ifndef WORDLISTH
#define WORDLISTH
struct WordNode{
char *word;
unsigned long count;
struct WordNode *next;
};
void printWordList( struct WordNode *wordListHead);
struct WordNode *addWord(char* word , struct WordNode *wordListead);
#endif
other functions for reference:
//function to check if word is in linked list
bool doesWordExist(char* myword, struct WordNode *wordListHead){
while (wordListHead != NULL){
if(strcmp(wordListHead->word, myword) == 0){
return 0;
}
wordListHead= wordListHead-> next;
}
return 1;
}
//function to update the count of word
void updateCount(char* myword, struct WordNode *wordListHead){
while (wordListHead != NULL){
if(strcmp(wordListHead->word, myword) == 0){
//update count value
//capture current count and add 1
int curcount = wordListHead->count;
int newcount = curcount + 1;
wordListHead->count = newcount;
//printf("%d\n", newcount);
}
wordListHead= wordListHead-> next;
}
}
//function to print word list
//takes head node as argument
void printWordList( struct WordNode *wordListHead){
//WordNode *toyptr = wordListHead;
while (wordListHead != NULL){
printf("%s\n", wordListHead->word);
printf("%ld\n", wordListHead->count);
wordListHead = wordListHead -> next;
}
}
When you are storing token into your struct, you are using a pointer that is part of the input buffer.
On a new input line, the tokens gathered from previous lines will be corrupted/trashed.
You need to allocate space to store the token in the struct on the heap. Use strdup for that.
So, in addWord, you want:
current->word = strdup(word);
And in main, you want:
wordListHead->word = strdup(token);
UPDATE:
That's the primary issue. But, your code does a bunch of needless replication.
addWord doesn't handle an empty list. But, if it did there would be no need for main to have separate [replicated] code for the first word and subsequent words on the line.
The strcmp can be incorporated into addWord and it can "do it all". (i.e. a single scan of the list)
For doesWordExist, it returns a bool on a match. If it returned the pointer to the element that matched, updateCount would just have to increment the count [and not rescan the list]. I've updated those functions accordingly, but they are no longer needed due to the changes to addWord
Here's how I would simplify and refactor the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef int bool;
#ifdef DEBUG
#define dbgprt(_fmt...) \
printf(_fmt)
#else
#define dbgprt(_fmt...) do { } while (0)
#endif
//structure definition of linked list node
#ifndef WORDLISTH
#define WORDLISTH
struct WordNode {
char *word;
unsigned long count;
struct WordNode *next;
};
void printWordList(struct WordNode *wordListHead);
#endif
//function to add word to linked list
struct WordNode *
addWord(char *word, struct WordNode **list)
{
struct WordNode *curr;
struct WordNode *prev = NULL;
struct WordNode *newnode = NULL;
for (curr = *list; curr != NULL; curr = curr->next) {
if (strcmp(curr->word,word) == 0) {
newnode = curr;
break;
}
prev = curr;
}
// create new node
do {
// word already exists
if (newnode != NULL)
break;
newnode = malloc(sizeof(struct WordNode));
newnode->word = strdup(word);
newnode->count = 0;
newnode->next = NULL;
// attach to tail of list
if (prev != NULL) {
prev->next = newnode;
break;
}
// first node -- set list pointer
*list = newnode;
} while (0);
// increment the count
newnode->count += 1;
return newnode;
}
//function to check if word is in linked list
struct WordNode *
findWord(char *myword, struct WordNode *head)
{
struct WordNode *curr;
for (curr = head; curr != NULL; curr = curr->next) {
if (strcmp(curr->word,myword) == 0)
break;
}
return curr;
}
//function to update the count of word
void
updateCount(char *myword, struct WordNode *head)
{
struct WordNode *match;
match = findWord(myword,head);
if (match != NULL)
match->count += 1;
}
//function to print word list
//takes head node as argument
void
printWordList(struct WordNode *head)
{
struct WordNode *curr;
for (curr = head; curr != NULL; curr = curr->next) {
printf("%s", curr->word);
printf(" %ld\n", curr->count);
}
}
int
main(int argc, char **argv)
{
char *filename = argv[1];
FILE *fp = fopen(filename, "r");
printf("FILE: %s\n", filename);
if (fp == NULL) {
printf("Error: unable to open the file ");
printf("%s", filename);
return 1;
}
// These are our word delimiters.
char *delim = " \t\n,:;'\".?!#$-><(){}[]|\\/*&^%#!~+=_";
char line[1000];
char *token;
// create head pointer
struct WordNode *wordListHead = NULL;
// iterate through each line in file
while (fgets(line, sizeof(line), fp)) {
// seperate each word
// first word of line
char *bp = line;
while (1) {
token = strtok(bp, delim);
bp = NULL;
if (token == NULL)
break;
dbgprt("TOKEN1: %s\n", token);
addWord(token,&wordListHead);
}
}
printWordList(wordListHead);
return 0;
}
UPDATE #2:
Note that addWord and findWord replicate code. The first part of addWord is [essentially] duplicating what findWord does.
But, addWord can not just use findWord [which would be desirable] because findWord, if it fails to find a match returns NULL. In that case, it doesn't [have a way to] communicate back the last element (i.e. the "tail" of the list) which addWord needs to append to.
While we could add an extra argument to findWord to propagate this value back, a better solution is to create a different struct that defines a "list".
In the existing code, we are using a "double star" pointer to the head word node as a "list". Using a separate struct is cleaner and has some additional advantages.
We can just pass around a simple pointer to the list. We no longer need to worry about whether we should be passing a double star pointer or not.
Although we're only using a singly linked list, a separate list struct helps should we wish to convert the list to a doubly linked list [later on].
We just pass around a pointer to the list, and the list can keep track of the head of the list, the tail of the list, and the count of the number of elements in the list.
Linked lists lend themselves well to sorting with mergesort. The list count helps make that more efficient because it is much easier to find the "midpoint" of the list [which a mergesort would need to know].
To show the beginnings of the doubly linked list, I've added a prev element to the word struct. This isn't currently used, but it hints at the doubly linked version.
I've reworked all functions to take a pointer to a list, rather than a pointer to the head node.
Because the list struct has a tail pointer, addWord can now call findWord. If findWord does not find a match, addWord can simply use the head/tail pointers in the list to find the correct insertion point.
To simplify a bit more, I've changed the word node [and the list struct] to use some typedef statements.
Also, it's more usual/idiomatic to have the dominant struct pointer be the first argument, so I've reversed the order of the arguments on some of the functions.
Anyway, here's the [further] refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef int bool;
#ifdef DEBUG
#define dbgprt(_fmt...) \
printf(_fmt)
#else
#define dbgprt(_fmt...) do { } while (0)
#endif
//structure definition of linked list node
#ifndef WORDLISTH
#define WORDLISTH
// word frequency control
typedef struct WordNode Word_t;
struct WordNode {
const char *word;
unsigned long count;
Word_t *next;
Word_t *prev;
};
// word list control
typedef struct {
Word_t *head;
Word_t *tail;
unsigned long count;
} List_t;
void printWordList(List_t *list);
#endif
// create a list
List_t *
newList(void)
{
List_t *list;
list = calloc(1,sizeof(*list));
return list;
}
// function to check if word is in linked list
Word_t *
findWord(List_t *list,const char *myword)
{
Word_t *curr;
for (curr = list->head; curr != NULL; curr = curr->next) {
if (strcmp(curr->word,myword) == 0)
break;
}
return curr;
}
//function to add word to linked list
Word_t *
addWord(List_t *list,const char *word)
{
Word_t *match;
do {
// try to find existing word
match = findWord(list,word);
// word already exists
if (match != NULL)
break;
// create new node
match = malloc(sizeof(*match));
match->word = strdup(word);
match->count = 0;
match->next = NULL;
// attach to head of list
if (list->head == NULL)
list->head = match;
// append to tail of list
else
list->tail->next = match;
// set new tail of list
list->tail = match;
// advance list count
list->count += 1;
} while (0);
// increment the word frequency count
match->count += 1;
return match;
}
//function to update the count of word
void
updateCount(List_t *list,const char *myword)
{
Word_t *match;
match = findWord(list,myword);
if (match != NULL)
match->count += 1;
}
//function to print word list
//takes head node as argument
void
printWordList(List_t *list)
{
Word_t *curr;
for (curr = list->head; curr != NULL; curr = curr->next) {
printf("%s", curr->word);
printf(" %ld\n", curr->count);
}
}
int
main(int argc, char **argv)
{
char *filename = argv[1];
FILE *fp = fopen(filename, "r");
printf("FILE: %s\n", filename);
if (fp == NULL) {
printf("Error: unable to open the file ");
printf("%s", filename);
return 1;
}
// These are our word delimiters.
char *delim = " \t\n,:;'\".?!#$-><(){}[]|\\/*&^%#!~+=_";
char line[1000];
char *token;
// create list/head pointer
List_t *list = newList();
// iterate through each line in file
while (fgets(line, sizeof(line), fp) != NULL) {
// seperate each word
// first word of line
char *bp = line;
while (1) {
token = strtok(bp, delim);
bp = NULL;
if (token == NULL)
break;
dbgprt("TOKEN1: %s\n", token);
addWord(list,token);
}
}
printWordList(list);
return 0;
}
#Craig Estey has provided a great answer for you, so don't change your answer selection, but rather than just leave you a link in the comments, there are a couple of important ways of looking at list operations that may help, and you must use a memory/error checking program to validate your use of allocated memory, especially when dealing with list operations.
Take a node holding a string with a reference count of the additional number of times the string occurs, as in your case, e.g.
typedef struct node_t { /* list node */
char *s;
size_t refcnt;
struct node_t *next;
} node_t;
Iterating With Address of Node & Pointer to Node Eliminates Special Cases
Using both the address of the node and pointer to node is discussed in Linus on Understanding Pointers.
For example, where you need to check if (list->head == NULL) to add the first node to the list, if iterating with both the address of the node and pointer to node, you simply assign the allocated pointer to your new node to the address of the current node. This works regardless whether it is the first, middle or last node. It also eliminates having to worry about what the previous node was when removing nodes from the list. At the node to delete, you simply assign the contents of the next node to the current address and free the node that was originally there. This reduces your add node function to something similar to:
/** add node in sort order to list.
* returns pointer to new node on success, NULL otherwise.
*/
node_t *add_node (node_t **head, const char *s)
{
node_t **ppn = head, /* address of current node */
*pn = *head; /* pointer to current node */
while (pn) { /* iterate to last node */
if (strcmp (pn->s, s) == 0) { /* compare node string with string */
pn->refcnt += 1; /* increment ref count */
return *ppn;
}
ppn = &pn->next; /* ppn to address of next */
pn = pn->next; /* advance pointer to next */
}
return *ppn = create_node (s); /* allocate and return node */
}
(note: by delaying allocation for the new node and string (create_node (s) above), you avoid allocating until you know the string needs to be added -- simplifying memory handling)
As mentioned in the comments above, this combines your doesWordExist() traversal of the list and your addWord() traversal to find the end into a single traversal. If there are hundreds of thousands of nodes in your list, you don't want to traverse the list multiple times.
Using strdup() is Fine, but know it's POSIX not standard C
strdup() is handy for duplicating strings and assigning the result to a new pointer, but strdup() is provided by POSIX, so not all implementation will provide it. Additionally, strdup() allocates memory, so just as with any function that allocates memory, you must check that the result is not NULL before using the pointer that is returned. You can avoid the potential portability issue by writing a short equivalent. For example in the create_node() shown above, it does:
/** helper function to allocate node, and storage for string.
* copies string to node and initializes node->next pointer NULL
* and node->refcnt zero. returns pointer to allocated node on success,
* NULL otherwise.
*/
node_t *create_node (const char *s)
{
size_t len = strlen (s); /* get string length */
node_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate EVERY allocation */
perror ("create_node: malloc node");
return NULL;
}
if (!(node->s = malloc (len + 1))) { /* allocate for string */
perror ("create_node: malloc node->s");
free (node); /* on failure, free node before returning NULL */
return NULL;
}
memcpy (node->s, s, len+1); /* copy string to node */
node->refcnt = 0; /* initialize ref count */
node->next = NULL; /* initialze next NULL */
return node; /* return allocated & initialized node */
}
Freeing Allocated Memory
Any time you write code creating data structures, they need to be able to clean up after themselves in the event you want to remove a single node, or are done using the list. This becomes imperative when you create lists, etc. that are declared and used solely within functions below main() as the memory isn't freed on the function return. With main(), on return the program exits and will free all allocated memory. However if the list is created and used solely below main() a memory leak will result each time that function is called if the list memory is not freed before return. A function that frees all memory is short and easy to write, e.g.
/** delete all nodes in list */
void del_list (node_t *head)
{
node_t *pn = head; /* pointer to iterate */
while (pn) { /* iterate over each node */
node_t *victim = pn; /* set victim to current */
pn = pn->next; /* advance pointer to next */
free (victim->s); /* free current string */
free (victim); /* free current node */
}
}
(**no need to worry about the refcnt since you are deleting the list)
A short example including all of these points, as well as a function del_node() to remove a single node from the list (or reduce the refcnt without removing the node if the refcnt is non-zero) can be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
typedef struct node_t { /* list node */
char *s;
size_t refcnt;
struct node_t *next;
} node_t;
/** helper function to allocate node, and storage for string.
* copies string to node and initializes node->next pointer NULL
* and node->refcnt zero. returns pointer to allocated node on success,
* NULL otherwise.
*/
node_t *create_node (const char *s)
{
size_t len = strlen (s); /* get string length */
node_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate EVERY allocation */
perror ("create_node: malloc node");
return NULL;
}
if (!(node->s = malloc (len + 1))) { /* allocate for string */
perror ("create_node: malloc node->s");
free (node); /* on failure, free node before returning NULL */
return NULL;
}
memcpy (node->s, s, len+1); /* copy string to node */
node->refcnt = 0; /* initialize ref count */
node->next = NULL; /* initialze next NULL */
return node; /* return allocated & initialized node */
}
/** add node in sort order to list.
* returns pointer to new node on success, NULL otherwise.
*/
node_t *add_node (node_t **head, const char *s)
{
node_t **ppn = head, /* address of current node */
*pn = *head; /* pointer to current node */
while (pn) { /* iterate to last node */
if (strcmp (pn->s, s) == 0) { /* compare node string with string */
pn->refcnt += 1; /* increment ref count */
return *ppn;
}
ppn = &pn->next; /* ppn to address of next */
pn = pn->next; /* advance pointer to next */
}
return *ppn = create_node (s); /* allocate and return node */
}
/** print all nodes in list */
void prn_list (node_t *head)
{
if (!head) { /* check if list is empty */
puts ("list-empty");
return;
}
for (node_t *pn = head; pn; pn = pn->next) /* iterate over each node */
printf ("%-24s %4zu\n", pn->s, pn->refcnt); /* print string an refcount */
}
/** delete node with string s from list (for loop) */
void del_node (node_t **head, const char *s)
{
node_t **ppn = head; /* address of node */
node_t *pn = *head; /* pointer to node */
for (; pn; ppn = &pn->next, pn = pn->next) {
if (strcmp (pn->s, s) == 0) { /* does string match */
if (pn->refcnt) { /* ref count > 0 */
pn->refcnt -= 1; /* decrement ref count */
return; /* done */
}
*ppn = pn->next; /* set content at address to next */
free (pn); /* free original pointer */
break;
}
}
}
/** delete all nodes in list */
void del_list (node_t *head)
{
node_t *pn = head; /* pointer to iterate */
while (pn) { /* iterate over each node */
node_t *victim = pn; /* set victim to current */
pn = pn->next; /* advance pointer to next */
free (victim->s); /* free current string */
free (victim); /* free current node */
}
}
int main (int argc, char **argv) {
char buf[MAXC]; /* read buffer */
const char *delim = " \t\n.,;?!()"; /* strtok delimiters */
node_t *list = NULL; /* pointer to list (must initialize NULL */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) /* read all lines in file */
/* tokenize line based on delim */
for (char *p = strtok (buf, delim); p; p = strtok (NULL, delim)) {
if (ispunct(*p)) /* if word is punctionation, skip */
continue;
add_node (&list, p); /* add node or increment refcnt */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
puts ("string refcnt\n" /* heading */
"-------------------------------");
prn_list (list); /* print contents of list */
del_list (list); /* free all list memory */
return 0;
}
Take a look at the characters included in delim to use with strtok() as well as the use of ispunct() to skip tokens that end up beginning with punctuation (which allows hyphenated words, but skips hyphens used alone as sentence continuations, etc....)
Example Input File
$ cat dat/tr_dec_3_1907.txt
No man is above the law and no man is below it;
nor do we ask any man's permission when we require him to obey it.
Obedience to the law is demanded as a right; not asked as a favor.
(Theodore Roosevelt - December 3, 1907)
Example Use/Output
$ ./bin/lls_string_nosort_refcnt dat/tr_dec_3_1907.txt
string refcnt
-------------------------------
No 0
man 1
is 2
above 0
the 1
law 1
and 0
no 0
below 0
it 1
nor 0
do 0
we 1
ask 0
any 0
man's 0
permission 0
when 0
require 0
him 0
to 1
obey 0
Obedience 0
demanded 0
as 1
a 1
right 0
not 0
asked 0
favor 0
Theodore 0
Roosevelt 0
December 0
3 0
1907 0
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/lls_string_nosort_refcnt dat/tr_dec_3_1907.txt
==8528== Memcheck, a memory error detector
==8528== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8528== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8528== Command: ./bin/lls_string_nosort_refcnt dat/tr_dec_3_1907.txt
==8528==
string refcnt
-------------------------------
No 0
man 1
is 2
above 0
the 1
law 1
and 0
no 0
below 0
it 1
nor 0
do 0
we 1
ask 0
any 0
man's 0
permission 0
when 0
require 0
him 0
to 1
obey 0
Obedience 0
demanded 0
as 1
a 1
right 0
not 0
asked 0
favor 0
Theodore 0
Roosevelt 0
December 0
3 0
1907 0
==8528==
==8528== HEAP SUMMARY:
==8528== in use at exit: 0 bytes in 0 blocks
==8528== total heap usage: 73 allocs, 73 frees, 6,693 bytes allocated
==8528==
==8528== All heap blocks were freed -- no leaks are possible
==8528==
==8528== For counts of detected and suppressed errors, rerun with: -v
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
You already have a solution to your immediate problem, but going forward in your project consider some of the tips above to eliminate multiple traversals of your list, and the portability (and validation) issues surrounding strdup(). Good luck with your coding.
So I have code that I will show below
struct GraphicElement {
char* fileName;
struct GraphicElement* pNext;
};
struct RasterGraphic {
struct GraphicElement* GraphicElements;
};
AND ALSO
void InsertGraphicElement(struct RasterGraphic* pA)
{
int counter = 1;
int response = 0;
char tempString[256];
struct GraphicElement *newNode = malloc(sizeof(*newNode));
if (newNode == NULL) return;
newNode->fileName = malloc(256 * sizeof(char));
if (newNode->fileName == NULL) return;
newNode->pNext = NULL;
printf("Insert a GraphicElement in the RasterGraphic\nPlease enter the GraphicElement filename: ");
scanf("%s", newNode->fileName);
if (pA->GraphicElements == NULL)
{
pA->GraphicElements = newNode;
printf("This is the first GraphicElement in the list\n");
}
else
{
struct GraphicElement *tempHead = pA->GraphicElements;
while (tempHead->pNext != NULL)
{
tempHead = tempHead->pNext;
counter++;
}
printf("There are %d GraphicElement(s) in the list. Please specify the position (<= %d) to insert at :", counter, counter);
scanf("%d", &response);
if (response == counter) {
tempHead->pNext = newNode;
return;
}
}
return;
}
So as you can see I have a struct definition and then a function to insert a node into the list. I have it so that if its the first node it inserts and tells the user it is the first element in the list. Where I am having a problem is adding more elements. Right now the code has no problems adding new nodes in sequence. The user is asked where they would like to insert into the list. Right now as long as they choose to just add it in sequence it works perfectly. I've tried a lot of things i just cant figure out the logic to loop through and be able to add a node to the middle of the list so an example output would be like this..
List contains 1, 2, 3
the user adds a fourth element 4 but wants to add it to spot 1 which is where the 2 is sitting so the new updated list would look like 1, 4, 2, 3.
There is no magic to inserting anywhere within the linked list, the only real challenge is keeping your pointers straight when handling three conditions:
inserting the first node (or new first node);
inserting a node between at some position between the existing first and last node; and
inserting at the end of the list.
The first case, inserting the first (or new first) node simply requires allocating a new node and either inserting it is the first node in the list, or if the head node already exists, setting newnode->next = head; and head = newnode;
Inserting at the end is not much different, you just iterate to the last node and set last->next = newnode;
The case of inserting a node in between takes a bit more thought. The easiest way to keep your pointers straight is to pull out a pencil and paper and draw out your pointer diagram of your list and then break the list in two where you need to insert the new node, and figure out the steps required before you pickup the keyboard (it will go a lot easier that way)
Nothing fancy is needed, just a block diagram with your next (or your pNext) pointer connecting the nodes. A simple list will two nodes (A and B) will do, e.g.
A B
+------+ +------+
| node | | node |
| next |--> | next |-->NULL
+------+ +------+
Then just break the list at the point where the new node will be inserted, e.g.
A B
+------+ | +------+
| node | / | node |
| next |--> \ | next |-->NULL
+------+ / +------+
|
new
+------+
| node |
| next |-->
+------+
That allows you to visualize the needed steps. Locate node A, set new->next = B;, set A->next = new; (note: you must stop at the node before the location you wish to insert the new node) The result will then be:
A new B
+------+ +------+ +------+
| node | | node | | node |
| next |--> | next |--> | next |-->NULL
+------+ +------+ +------+
When an understanding of exactly what you need to do to handle the insertion, now pick up the keyboard and implement that logic.
Since you will have a single insert function to handle all three cases, as #WhozCraig commented, when handling list operations (or pointers in general where you may need to assign a new block of memory within a function changing the address of the pointer), it helps to pass the address of the pointer to your function (so that your function receives the pointer itself -- instead of a copy of the pointer).
Whether in your case you need to initially allocate for the list wrapper, or in a simple list where you may assign a new node as the first node in the list, passing the address of the pointer allows the change within the function without having to return and assign the return as the new address back in the calling function.
Adding a couple of typedefs gelement_t for your struct GraphicElement and rgraphic_t for struct RasterGraphic types, both to cut down on typing an remove some of the MixedCase style awkwardness, you can think about your InsertGraphicElement function in the following way.
First, your InsertGraphicElement function will either succeed or fail, so choose a meaningful return type that can indicate success or failure. When dealing with lists, returning a pointer to the newly inserted node is helpful and allows a return of NULL in the even of failure, e.g.
gelement_t *InsertGraphicElement (rgraphic_t **pA)
Since you are passing a pointer to pointer to your RasterGraphic struct, you can add data to the struct to make it a bit more useful as a wrapper to your actual list. Adding a node-counter is a convenient way to keep track of the number of nodes in a singly-linked list without having to iterate over the list each time, e.g.
typedef struct RasterGraphic {
size_t nelements;
gelement_t *GraphicElements;
} rgraphic_t;
Within your function you should validate that you have an allocated pointer, and if not, then allocate for the RasterGraphic struct, e.g.
int position = 0;
if (!*pA) { /* if list NULL - allocate new list */
puts ("allocating new list.");
*pA = malloc (sizeof **pA);
if (!*pA) { /* validate every allocation */
perror ("malloc-*list");
exit (EXIT_FAILURE);
}
(*pA)->nelements = 0; /* initialize values */
(*pA)->GraphicElements = NULL;
}
Next you can allocate and validate the new node to add either as the first node or at position, e.g.
gelement_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate */
perror ("malloc-node");
exit (EXIT_FAILURE);
}
Next collect your filename and position information and set node->fileName to a newly allocated block holding the filename entered by the user (a couple of helper-functions make this much easier), e.g.
node->fileName = get_filename_stdin(); /* request filename */
if (!node->fileName) { /* validate */
free (node);
return NULL;
}
node->pNext = NULL; /* set next pointer NULL */
position = get_int_stdin (*pA); /* request position */
You are now at the point within the function where you handle the 3-cases identified at the beginning of the answer. If there is no (*pA)->GraphicElements node yet, you will add the first node (so you don't need to ask for position). If it isn't the first node, and the position requested is 0, you insert as a new first node. (both can be handled in a single case)
If the position requested is greater than zero, then you iterate to the node before the insertion point and insert as indicated with the diagram above and insert the node there.
One approach would be:
gelement_t *p = (*pA)->GraphicElements;
if (!p || position == 0) { /* insert as new head */
node->pNext = p;
(*pA)->GraphicElements = node;
}
else { /* insert at position (default end) */
int n = 0;
while (n < position - 1 && p->pNext) { /* locate node before */
p = p->pNext;
n++;
}
node->pNext = p->pNext; /* set node->pNext to current pNext */
p->pNext = node; /* set current pNext to node */
}
(*pA)->nelements++; /* increment number of elements in list */
Which will handle your insertion based on the user input. All that remains is to:
return node; /* meaningful return to indicate success/failure */
}
(note: when you are comfortable with the list operation logic, it helps to break this function up into several functions that handle the operations individually, like create_list(), create_node() and add_node(). (where you create your RasterGraphic list, your GraphicElement node and finally add that node at a given position in the list -- that is left to you)
Putting it altogether and adding the helper functions, a short example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> /* for PATH_MAX */
typedef struct GraphicElement {
char* fileName;
struct GraphicElement* pNext;
} gelement_t;
typedef struct RasterGraphic {
size_t nelements;
gelement_t *GraphicElements;
} rgraphic_t;
char *get_filename_stdin (void)
{
char filename[PATH_MAX] = "";
char *newname = NULL;
fputs ("enter filename : ", stdout);
if (fgets (filename, PATH_MAX, stdin)) {
size_t len = strlen (filename);
if (len && filename[len-1] == '\n') {
filename[--len] = 0;
if (!len) {
fputs ("error: filename empty.\n", stderr);
return NULL;
}
}
else if (len == PATH_MAX) {
fputs ("error: filename exceeds PATH_MAX\n", stderr);
return NULL;
}
newname = malloc (len + 1);
if (!newname) {
perror ("malloc-newname");
exit (EXIT_FAILURE);
}
memcpy (newname, filename, len + 1);
}
return newname;
}
int get_int_stdin (rgraphic_t *list)
{
char buf[PATH_MAX];
int pos = 0;
if (!list->nelements) {
puts ("inserting as head.");
return 0;
}
fputs ("index to insert: ", stdout);
if (fgets (buf, PATH_MAX, stdin))
if (sscanf (buf, "%d", &pos) != 1 || pos < 0 ||
pos > (long)list->nelements)
return list->nelements;
return pos;
}
gelement_t *InsertGraphicElement (rgraphic_t **pA)
{
int position = 0;
if (!*pA) { /* if list NULL - allocate new list */
puts ("allocating new list.");
*pA = malloc (sizeof **pA);
if (!*pA) { /* validate every allocation */
perror ("malloc-*list");
exit (EXIT_FAILURE);
}
(*pA)->nelements = 0; /* initialize values */
(*pA)->GraphicElements = NULL;
}
gelement_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate */
perror ("malloc-node");
exit (EXIT_FAILURE);
}
node->fileName = get_filename_stdin(); /* request filename */
if (!node->fileName) { /* validate */
free (node);
return NULL;
}
node->pNext = NULL; /* set next pointer NULL */
position = get_int_stdin (*pA); /* request position */
gelement_t *p = (*pA)->GraphicElements;
if (!p || position == 0) { /* insert as new head */
node->pNext = p;
(*pA)->GraphicElements = node;
}
else { /* insert at position (default end) */
int n = 0;
while (n < position - 1 && p->pNext) { /* locate node before */
p = p->pNext;
n++;
}
node->pNext = p->pNext; /* set node->pNext to current pNext */
p->pNext = node; /* set current pNext to node */
}
(*pA)->nelements++; /* increment number of elements in list */
return node; /* meaningful return to indicate success/failure */
}
/* loop over list printing values */
void prn_list (rgraphic_t *list)
{
size_t n = 0;
gelement_t *node = list->GraphicElements;
printf ("\n\n%zu nodes in list\n", list->nelements);
for (; node; node = node->pNext)
printf ("%2zu: %s\n", 1 + n++, node->fileName);
}
/* loop over list freeing memory (pay attention to victim) */
void free_list (rgraphic_t *list)
{
gelement_t *node = list->GraphicElements;
while (node) {
gelement_t *victim = node;
node = node->pNext;
free (victim->fileName);
free (victim);
}
free (list);
}
int main (void) {
rgraphic_t *list = NULL;
puts ("\nNOTE: pressing [Enter] for index - inserts at end!\n"
" [Ctrl+d] at \"filename: \" prompt to end input.\n");
while (InsertGraphicElement(&list)) {} /* create list/insert nodes */
prn_list (list); /* print list */
free_list (list); /* free list */
}
Example Use/Output
$ ./bin/ll_single_insert_ptp
NOTE: pressing [Enter] for index - inserts at end!
[Ctrl+d] at "filename: " prompt to end input.
allocating new list.
enter filename : one
inserting as head.
enter filename : four
index to insert: 1
enter filename : two
index to insert: 1
enter filename : three
index to insert: 2
enter filename : five
index to insert:
enter filename :
5 nodes in list
1: one
2: two
3: three
4: four
5: five
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/ll_single_insert_ptp
==9747== Memcheck, a memory error detector
==9747== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==9747== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==9747== Command: ./bin/ll_single_insert_ptp
==9747==
NOTE: pressing [Enter] for index - inserts at end!
[Ctrl+d] at "filename: " prompt to end input.
allocating new list.
enter filename : one
inserting as head.
enter filename : four
index to insert: 1
enter filename : two
index to insert: 1
enter filename : three
index to insert: 2
enter filename : five
index to insert:
enter filename :
5 nodes in list
1: one
2: two
3: three
4: four
5: five
==9747==
==9747== HEAP SUMMARY:
==9747== in use at exit: 0 bytes in 0 blocks
==9747== total heap usage: 12 allocs, 12 frees, 136 bytes allocated
==9747==
==9747== All heap blocks were freed -- no leaks are possible
==9747==
==9747== For counts of detected and suppressed errors, rerun with: -v
==9747== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
So I'm having a bit of trouble with Linked Lists in C. In general, I get the concept and and the algorithm since I have learned the ideas already in Java. But it seems like it's a different story in C, as we also take into consideration memory allocation.
Anyway, I have this code down here:
while (curr != NULL) {
printf("%s\n", (*curr).name);
curr = (*curr).next;
}
Where curr is a struct Student and one of the "attributes" of Student is name. So suppose I have already added in my nodes to the list. And when I execute the above, I seem to be getting the same names altogether. Below is my code for adding the nodes into the linked list:
void insertNode(char *name, int idNum, char sex) {
struct Student *s;
s = malloc(sizeof(struct Student));
//not entirely sure if this is the right way to do it
if (s == NULL) {
printf("Memory allocation failed.");
return;
}
(*s).name = name;
(*s).idNum = idNum;
(*s).sex = sex;
(*s).next = head; //head is the start of the list
head = s; //inserting the node at the beginning of the list
curr = head; //place the current pointer at the start of list
}
Basically it seems like I have no problems with the int and char. If I have {"Alice", 1000, 'F'} -> {"Bob", 1001, 'M'} -> {"Charlie", 1002, 'M'} in my list, I noticed the last name added to the list, Alice, will be the one that's being printed out. So the printout will be:
Alice
Alice
Alice
Am I doing something wrong here? Is there something I'm missing? I'm a beginner in C and still learning by myself. Thanks so much for any help and advice!
You need to keep track of the head pointer.
Here is the complete working code:
#include <stdio.h>
#include <stdlib.h>
struct Student
{
char * name;
int idNum;
char sex;
struct Student * next;
};
struct Student * head=NULL;
struct Student *
insertNode (char *name, int idNum, char sex)
{
struct Student *s;
s = malloc (sizeof (struct Student));
if (s == NULL)
{
printf ("Memory allocation failed.");
return NULL;
}
(*s).name = name;
(*s).idNum = idNum;
(*s).sex = sex;
(*s).next = head; //head is the start of the list
head = s;
return head;
}
int
main ()
{
insertNode("Alice", 1, 'F');
insertNode("Peter", 2, 'M');
insertNode("Mike", 3, 'M');
while (head != NULL)
{
printf ("%s\n", (*head).name);
head = (*head).next;
}
}
Output:
Mike
Peter
Alice
However, there are plenty of improvements that can still be done in your code. In particular, the notation (*s).name has a shorthand in C and that is s->name which is more clean. You can also avoid the global head variable and instead pass its pointer so that its changed value can be maintained in between calls.
(*s).name = name; sets the name in the struct to the local variable name which will become invalid at the end of the insertNode function.
You need to make a copy of the name.
You can use s->name = strdup(name); (Just rememeber you need to free s->name when you delete a node.
perhaps a simpler method whice you get your head around C would be to make name in the student node an array char name[32]; or similar. Then you would strcpy() into it but have one less thing to free.
Your question is a bit unclear as to the exact source of the problem. The answer by Syed does a good job identifying a solution, but there are some subtleties you need to be aware of as you are learning linked-lists.
First, your assignment of s->name = name; will only work if the names are string literals where you assign the address to the beginning of a string stored in read-only memory to s->name.
If you are reading values from a file (or stdin) and passing a pointer containing name to insertnode, then all of your nodes s->name will hold the pointer address, which, if it is still in scope will point to the storage containing the Last Name Read, and if your pointer used to pass name to insertnode has gone out of scope, you will invoke Undefined Behavior attempting to access s->name.
As correctly noted in the comments, the way to handle the situation is to allocate storage for each name passed to insertnode, assign the starting address to s->name and copy name to s->name. You can use strdup if you have that available, otherwise a simple allocation and strcpy is all that is required:
size_t len = strlen (name); /* get length of name */
/* allocate/validate storage for name */
if ((s->name = malloc (len + 1)) == NULL) {
perror ("malloc - name");
free (s);
return NULL;
}
strcpy (s->name, name); /* copy name to s->name */
(note: since strdup allocates memory (e.g. in s->name = strdup(name);), you should still validate s->name is not NULL after the call. Also note that strings is C are terminated with the nul-characer, '\0'. Therefore, in order to allocate enough storage to hold name plus the nul-terminating character, you must allocate strlen (name) + 1 bytes.)
Next, while 100% OK, your insert-node-before head in a singly-linked list will have the affect of reversing the order of the nodes inserted into your list. While this may not matter for your implementation, it may be surprising to you when you do print the list. This does save iterating to the next free node on insert, but sacrifices input order to do so.
An alternative, would be to insert the nodes in order by simply checking if head is NULL, and if so, insert the first node, otherwise, iterate until list->next = NULL and insert the new node as list->next. (this also requires that you initialize all s->next nodes NULL when you allocate storage for them. A simple implementation (making use of a typedef for struct student for convenience), would look similar to:
/* returns pointer to new node on success, or NULL on failure */
student *insertnode (student **head, char *name, int idnum, char sex)
{
student *s = malloc (sizeof *s); /* allocate new node */
if (s == NULL) { /* validate allocation */
perror ("malloc - s");
return NULL;
}
/* populate new node */
size_t len = strlen (name); /* get length of name */
/* allocate/validate storage for name */
if ((s->name = malloc (len + 1)) == NULL) {
perror ("malloc - name");
free (s);
return NULL;
}
strcpy (s->name, name); /* copy name to s->name */
// s->name = name; /* only works for string literals */
s->idnum = idnum;
s->sex = sex;
s->next = NULL; /* always initialize ->next NULL */
if (*head == NULL) { /* handle add 1st node */
*head = s;
}
else { /* handle add rest */
student *iter = *head; /* declare pointer to head */
while (iter->next != NULL) /* while ->next not null */
iter = iter->next; /* get next node */
iter->next = s; /* set iter->next to new node */
}
return s; /* head never changes, return current node
to indicate success/failure of insert. */
}
Also, you should avoid declaring global variables. There is no reason your list shouldn't be declared in main() and a pointer (or address of the pointer if the list address could change) be passed as a parameter to any function that needs to operate on the list. Just move the declaration of head inside main(), and then add a pointer to pointer to list as a parameter for insertnode (as was done above).
You should free() all memory you allocate. (yes, this happens at exit, but building good habits now of preserving a pointer to the beginning address of all memory you allocate, and freeing that memory when it is no longer needed will pay dividends and your project grow in size.
Finally, when you iterate over your list, you must use a separate pointer. Otherwise, if you iterate using head, it is a one-way street. When you iterate until head == NULL, you have lost the only reference you had to all the memory you allocated and have no way of getting them back. Simply use a temporary pointer, e.g.
student *iter = head; /* use separate pointer to iterate list */
while (iter != NULL) {
printf ("%-8s %4d %c\n", iter->name, iter->idnum, iter->sex);
iter = iter->next;
}
(of course on your final trip over the list to free all the list memory -- it doesn't matter what you use -- there won't be any more list when you are done)
Putting it altogether, you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct student { /* typedef makes declarations easier */
struct student *next; /* pointers 1st limits size to 24-bytes */
char *name, sex;
int idnum;
} student;
/* returns pointer to new node on success, or NULL on failure */
student *insertnode (student **head, char *name, int idnum, char sex)
{
student *s = malloc (sizeof *s); /* allocate new node */
if (s == NULL) { /* validate allocation */
perror ("malloc - s");
return NULL;
}
/* populate new node */
size_t len = strlen (name); /* get length of name */
/* allocate/validate storage for name */
if ((s->name = malloc (len + 1)) == NULL) {
perror ("malloc - name");
free (s);
return NULL;
}
strcpy (s->name, name); /* copy name to s->name */
// s->name = name; /* only works for string literals */
s->idnum = idnum;
s->sex = sex;
s->next = NULL; /* always initialize ->next NULL */
if (*head == NULL) { /* handle add 1st node */
*head = s;
}
else { /* handle add rest */
student *iter = *head; /* declare pointer to head */
while (iter->next != NULL) /* while ->next not null */
iter = iter->next; /* get next node */
iter->next = s; /* set iter->next to new node */
}
return s; /* head never changes, return current node
to indicate success/failure of insert. */
}
int main (void) {
student *head = NULL;
insertnode (&head, "Alice", 1000, 'F'); /* insert nodes */
insertnode (&head, "Peter", 1001, 'M');
insertnode (&head, "Mike", 1002, 'M');
student *iter = head; /* use separate pointer to iterate list */
while (iter != NULL) {
printf ("%-8s %4d %c\n", iter->name, iter->idnum, iter->sex);
iter = iter->next;
}
/* free allocated memory */
while (head != NULL) { /* freeing list, pointer used doesn't matter */
student *victim = head; /* save pointer to node to delete */
head = head->next; /* move to next node */
free (victim->name); /* free storage for name */
free (victim); /* free storage for node */
}
}
(note: while not an error, the standard coding style for C avoids the use of camelCase or MixedCase variable names in favor of all lower-case while reserving upper-case names for use with macros and constants. It is a matter of style -- so it is completely up to you, but failing to follow it can lead to the wrong first impression in some circles.)
Example Use/Output
$ ./bin/ll_head_next
Alice 1000 F
Peter 1001 M
Mike 1002 M
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/ll_head_next
==30229== Memcheck, a memory error detector
==30229== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==30229== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==30229== Command: ./bin/ll_head_next
==30229==
Alice 1000 F
Peter 1001 M
Mike 1002 M
==30229==
==30229== HEAP SUMMARY:
==30229== in use at exit: 0 bytes in 0 blocks
==30229== total heap usage: 6 allocs, 6 frees, 89 bytes allocated
==30229==
==30229== All heap blocks were freed -- no leaks are possible
==30229==
==30229== For counts of detected and suppressed errors, rerun with: -v
==30229== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
I created this linked list but I am having trouble getting the char array and the ints at the same time. Any suggestions? I have been up and down in forums and google but I can't seem to get it to agree with anything.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node {
int id;
char name[40];
struct node *next;
struct node *prev;
} info;
int insert (int num, char *Name);
void print ();
struct node *root; // Declare root a pointer to the first node
int main () {
int n, Id, i;
char nAme[40];
char *name_ptr;
root = NULL; // List is empty
printf ("How many list entries do you want to make?\n");
scanf (" %d ", &n);
n = 2;
for (i = 0; i < n; i++) {
printf ("Enter name: ");
fgets (nAme, 40, stdin);
printf ("Enter id number: ");
scanf ("%d", &Id);
name_ptr = &nAme[0];
insert (Id, name_ptr);
print ();
}
}
int insert (int num, char *Name)
{
// Get memory
struct node *temp = (struct node *) malloc (sizeof (struct node));
if (temp == NULL) {
printf ("Some malloc problem.\n");
return 1;
}
temp->id = num;
strcpy (temp->name, Name);
temp->next = root;
root = temp;
}
void print ()
{
struct node *temp = root;
while (temp != NULL) {
printf ("%d\n%s\n", temp->id, temp->name);
temp = temp->next;
}
printf ("\n");
}
Sorry I'm a bit new to this site, so the formatting might say javascript, but its C as you can probably tell.
There are a number of problems conspiring against you. The first, important, but not a show stopper, is fgets will read up to and including the newline. Which means all of your nAme (presuming you meant name) entries have an embedded newline at the end. This will cause all kinds of havok when you attempt to search for or compare specific names in your list.
A huge issue is you have no validation on whether you even receive input to add to your list from the user. Always, always validate user input. That means checking at minimum that you actually have input to store. e.g.
printf ("Number of list entries to make?: ");
if (scanf (" %d%*c", &n) != 1) {
fprintf (stderr, "error: invalid no. of entries.\n");
return 1;
}
for (i = 0; i < n; i++) {
printf ("Enter name: ");
if (!fgets (name, MAXC, stdin)) {
fprintf (stderr, "error: invalid name.\n");
return 1;
}
rmcrlf (name); /* strip trailing newline from name */
printf ("Enter id number: ");
if (scanf ("%d%*c", &id) != 1) {
fprintf (stderr, "error: invalid ID.\n");
return 1;
}
insert (id, name);
}
(note: the rmcrlf (remove carriage-return newline) function trims the trailing new line or erroneous carriage return from the name value)
The more substantive issues being apparent confusion on what type of list you are creating (single/double linked, traditional head/tail, circular, what?) The difference being that a traditional head/tail will use a NULL value to mark the end/beginning of the list, while a circular list points the last node to the first allowing iteration from any point around the list.
You appear to want a doubly-linked list with your prev and next pointers, but you make absolutely no attempt to handle the prev pointer in any way. It is unclear whether you want a circular or head/tail NULL list. Regardless which you choose, you must account for setting the values for your prev and next pointer with each insert you do. It makes a difference whether you are setting the 1st (and 2nd on a circular list) compared to the remaining insertions. The following is an example of the insert function that will correctly handle additions to a circular list. A head/tail NULL list does not need specific handling of the second node.
struct node *insert (int num, char *name)
{
struct node *temp = malloc (sizeof *temp); /* allocate node */
if (!temp) {
fprintf (stderr, "error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
temp->id = num;
strcpy (temp->name, name);
if (!root) { /* empty list - insert first node */
temp->next = temp->prev = temp;
root = temp;
}
else if (root == root->next) { /* insert 2nd node */
temp->next = root;
root->prev = temp;
root->next = temp;
temp->prev = root;
}
else { /* insert all remaining nodes at end */
root->prev->next = temp;
temp->prev = root->prev;
temp->next = root;
root->prev = temp;
}
return temp;
}
(note: the function type being struct node *. It is of little use to return an int value here. You can judge success/failure by returning NULL in the event of a failure and you have the inserted node available for any post-insert processing required. Also, do NOT cast the return of malloc and you need only take the sizeof the object pointed to by the pointer being allocated)
Don't use magic numbers in your code. They make the code harder to maintain. If you need a constant to set the maximum characters in name, then define one with #define MAXC 40 or declare a global enum to define the constant (e.g. enum { MAXC = 40 };).
Putting those pieces together and fixing a few more nits, you could code your doubly-linked circular list something like the following example. You can simplify the code slightly by simply using a traditional head/tail NULL list instead.
Look the code over closely and understand the changes made (you need no name_ptr at all, and you don't set n=2; after taking input for n with scanf). Also note how the '\n' character scanf leaves in the input buffer (e.g. stdin) is handled. If you fail to account for the '\n', you will find scanf skipping over your input due to it taking the '\n' left in the input buffer as your input.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 40
struct node {
int id;
char name[MAXC];
struct node *next;
struct node *prev;
} info;
struct node *insert (int num, char *name);
void print (void);
void delete_list (void);
void rmcrlf (char *s);
struct node *root; /* Declare root a pointer to the first node */
int main (void) {
int i, id, n;
char name[MAXC];
root = NULL; /* List is empty */
printf ("Number of list entries to make?: ");
if (scanf (" %d%*c", &n) != 1) {
fprintf (stderr, "error: invalid no. of entries.\n");
return 1;
}
for (i = 0; i < n; i++) {
printf ("Enter name: ");
if (!fgets (name, MAXC, stdin)) {
fprintf (stderr, "error: invalid name.\n");
return 1;
}
rmcrlf (name);
printf ("Enter id number: ");
if (scanf ("%d%*c", &id) != 1) {
fprintf (stderr, "error: invalid ID.\n");
return 1;
}
insert (id, name);
}
print();
delete_list();
return 0;
}
struct node *insert (int num, char *name)
{
struct node *temp = malloc (sizeof *temp); /* allocate node */
if (!temp) {
fprintf (stderr, "error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
temp->id = num;
strcpy (temp->name, name);
if (!root) { /* empty list - insert first node */
temp->next = temp->prev = temp;
root = temp;
}
else if (root == root->next) { /* insert 2nd node */
temp->next = root;
root->prev = temp;
root->next = temp;
temp->prev = root;
}
else { /* insert all remaining nodes at end */
root->prev->next = temp;
temp->prev = root->prev;
temp->next = root;
root->prev = temp;
}
return temp;
}
void print (void)
{
struct node *temp = root;
printf ("\n Id Name\n ---- ---------------------\n");
while (temp->next != root) {
printf (" %4d %s\n", temp->id, temp->name);
temp = temp->next;
}
printf (" %4d %s\n", temp->id, temp->name);
printf ("\n");
}
/* free memory for all nodes in list */
void delete_list (void)
{
struct node *temp = root->next;
while (temp != root) {
struct node *victim = temp;
temp = temp->next;
free (victim);
}
if (temp) free (temp);
}
/** stip trailing newlines by overwriting with null-terminating char.
* str is modified in place.
*/
void rmcrlf (char *s)
{
if (!s || !*s) return;
for (; *s && *s != '\n'; s++) {}
*s = 0;
}
Example Use/Output
$ ./bin/ll_double_cir
Number of list entries to make?: 3
Enter name: cats
Enter id number: 3
Enter name: dogs
Enter id number: 17
Enter name: mice
Enter id number: 123
Id Name
---- ---------------------
3 cats
17 dogs
123 mice
Memory Use/Error Check
In any code your write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated. For Linux valgrind is the normal choice. Usage is simple, just run your code through it and check the results:
$ valgrind ./bin/ll_double_cir
==14595== Memcheck, a memory error detector
==14595== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==14595== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==14595== Command: ./bin/ll_double_cir
==14595==
Number of list entries to make?: 3
Enter name: cats
Enter id number: 3
Enter name: dogs
Enter id number: 17
Enter name: mice
Enter id number: 123
Id Name
---- ---------------------
3 cats
17 dogs
123 mice
==14595==
==14595== HEAP SUMMARY:
==14595== in use at exit: 0 bytes in 0 blocks
==14595== total heap usage: 3 allocs, 3 frees, 192 bytes allocated
==14595==
==14595== All heap blocks were freed -- no leaks are possible
==14595==
==14595== For counts of detected and suppressed errors, rerun with: -v
==14595== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Look over the changes and if you have any questions, just ask.
What is is not doing?
Lookign at the code:
root should be initialized to NULL.
insert could be called as inert(Id, nName); instread of insert(Id, name_ptr);
in insert, you should initalize ->prev = NULL;