Same values with different pointers in a linked list? - c

Why does this code output the same name for all the nodes in the linked list?
Program output
Insert number of users :
4
Mike
John
Bob
Alice
Name : Alice # Pointer :0x874ae0
Name : Alice # Pointer :0x874b00
Name : Alice # Pointer :0x874b20
Name : Alice # Pointer :(nil)
The idea behind this code is to take x number of user names and create a linked list then loop over the linked list and print each name with the pointer for the next name.
typedef struct node
{
char *name;
struct node *next;
} node;
int main(void)
{
int x;
printf("Insert number of users :\n"); // capture int from user
scanf("%i", &x);
char str[LENGTH];
node *n = malloc(sizeof(node));
if (n == NULL)
return 1;
node *start = n; // pointer to the start of the linked list
// loop for n times to capture names
for (int i = 0; i < x; i++)
{
scanf("%s", str); // capture string
n->name = str;
// reached end of loop
if (i == x-1)
n->next = NULL;
else
n->next = malloc(sizeof(node));
n = n->next;
}
for (node *tmp = start; tmp != NULL; tmp = tmp->next)
{
printf("Name : %s # Pointer :%p\n", tmp->name, tmp->next);
}
return 0;
}
A simple script to take the names of people and insert them into a linked list.

In this statement within the for loop
n->name = str;
the data member name of all nodes is set to the address of the first character of the array str declared like
char str[LENGTH];
So all nodes will point to the same array — that is, to the last stored string in this array after the for loop.
You need to create dynamically a copy of the string stored in the array for each node. Something like
#include <string.h>
//...
n->name = malloc( strlen( str ) + 1 );
strcpy( n->name, str );

Related

Linked list same value

I am trying to understand the linked list in C. So i am trying to write a program which will read from a file and create a linked list. But I hit a roadblock which I couldn't find a reason why.
Although I set the head value node *h to n only one time it looks like the value is automatically changing to the next value of n. To check I used printf at the end. All of them are returning the same result. Can anyone help please?
P.S - This is my first time using stackoverflow.
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
char *name;
struct node *next;
} node;
int main (void)
{
//load the file.
FILE *fp = fopen("dictionary.txt", "r");
if (fp == NULL)
{
printf("Unable to open the file\n");
}
char characters[45];
//initialize the linked list.
node *n , *t, *h;
int flag = 0;
while(fgets(characters, 45, fp) != NULL)
{
//define node for n
n = malloc(sizeof(node));
if (n == NULL)
{
printf("Out of memory!!!");
}
//set the value of n
n -> name = characters;
//set the temp & head value to n first time
if (flag == 0)
{
t = n;
h = n;
}
//set the temp -> next value to n after first time
else
{
t -> next = n;
}
flag = 1;
}
printf("%s\n", h -> name);
printf("%s\n", t -> name);
printf("%s\n", n -> name);
}
name member in your node struct is only a pointer to a string (character array).
each node you assign name to point to the very same character array:
char characters[45];
you should allocate the character array for any node:
#define MAX_LEN 45
typedef struct node
{
char name[MAX_LEN];
struct node *next;
} node;
and copy the string:
//set the value of n
strncpy(n -> name,characters, MAX_LEN);
// ensure null terminated
n->name[MAX_LEN-1] = '\0';

How to Copy Characters in a Linked List to an Array in C?

I am having an issue with copying the contents of the character array in a linked list to a regular array of characters. I have an issue with a segmentation fault that I am not sure why.
The program that I have created works when the character array in the linked list is only one character, but it does not work when it is greater than 1. The main issue occurs on line 62 ("array[index] = p -> word[count]"). I have tried using strcpy to copy each index of it into the character array but that as well produced an error that reads: "passing argument 2 of ‘strcpy’ makes pointer from integer without a cast".
However, when I use an assignment statement, I just get a segmentation fault. I am not sure why because I feel I've created enough memory that should be able to hold the contents of the linked list for the array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
char word[100];
struct node *next;
} ListNode;
int main ()
{
ListNode * head = NULL;
ListNode * tail = NULL;
for (int count = 0; count < 5; count++)
{
ListNode * temp = malloc (sizeof (*temp));
strcpy(temp -> word, "Hi");
temp -> next = NULL;
if (tail == NULL)
{
head = temp;
tail = temp;
}
else
{
tail->next = temp;
tail = temp;
}
}
char array[999]; // array that will hold the characters in the linked list
ListNode * p = head; //position of the current node
int count;
int index = 0;
// while p is still a node in the list
while(p != NULL)
{
if((int) strlen(p -> word) > 1) // checks if the string is longer than one character
{
count = 0; // initializes count as 0
while(count < (int) strlen(p -> word)) // counts how many characters are in the string
{
array[index] = p -> word[count]; // assings the words into charater array
count++; // increments the count
index++; // changes the index
}
}
else
{
array[index] = p -> word[0]; // copies p-word to array
index++; // changes the index in the array
p = p -> next;
}
}
return 0;
}
As mentioned before, the program works whenever the character array in the linked list is only 1, but a segmentation fault is produced when the number is greater than 1. Please let me know what I need to correct in this program. Thank you.
simplify your loops; for-loops allow you to keep the loop-machinery on one line
avoid special cases; there is nothing special about a one-char string
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
char word[100];
struct node *next;
} ListNode;
int main ()
{
ListNode * head = NULL;
ListNode * tail = NULL;
ListNode * p ;
int count;
int index ;
char array[999]; // array that will hold the characters in the linked list
for (count = 0; count < 5; count++)
{
ListNode * temp = malloc (sizeof *temp);
strcpy(temp->word, "Hi");
temp->next = NULL;
if (!tail) { head = temp; tail = temp; }
else { tail->next = temp; tail = temp; }
}
count=0;
for(p=head;p; p=p->next) { // walk the linked list
for(index=0; p->word[index]; index++) { // walk the string
array[count++] = p->word[index];
}
}
array[count++] = 0; // terminate
printf("%s\n", array);
return 0;
}

How can I alphabetize this linked list?

I am writing a program that has names and ages entered into it. The names can then be called and the age of the person will be printed out. If the person does not exist in the list it prints their age as -1. If a name is entered with a new age that is already in the list, the new entry is not added. Currently it appears the names are sorted by the order that I input them. How can I sort them alphabetically by only changing the code for the function add? This code is compileable and works as intended except for the non-alphabetized list.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct person {
char *name;
int age;
struct person *next;
} Person;
void print(Person *); // prints the entire list
Person *add(Person *, char *, int); // adds a new node to the list
int getAge(Person *, char *); // returns the age of the person or -1 if not found
int main(void) {
char input1[100];
int input2;
Person *myList = NULL;
printf("Enter a person's name (one word) and age : ");
scanf("%s %d", input1, &input2);
while (input2 != 0) {
myList = add (myList, input1, input2);
printf("\n\nThe list is now : "); print(myList);
printf("Enter a name (one word) and age, enter 'xxx' and 0 to exit : ");
scanf("%s %d", input1, &input2);
}
printf("\n\nThe final list is "); print(myList);
printf("\n\nEnter the name of a person to look up their age : ");
scanf("%s", input1);
while ( strcmp(input1, "xxx") != 0 ) {
printf("\t%s is %d years old\n", input1, getAge(myList, input1) );
printf("Enter a name to look up their age or 'xxx' to exit : ");
scanf("%s", input1);
}
return 0;
}
void print(Person *ptr) {
while (ptr) { printf("[%s-%d] ", ptr->name, ptr->age); ptr = ptr->next; }
printf("\n");
return;
}
//adds person to list if the person does not exist already
Person *add(Person *ptr, char *n, int a) {
Person *newNode = malloc( sizeof(Person) );
int duplicate = 1;
Person *dummy = ptr;
while (dummy) {
if(strcmp(dummy->name, n) == 0) {
printf("Name Already Exists in List! Please retry with other name..\n");
duplicate=-1;
break;
}
else
dummy = dummy->next;
}
if (duplicate!=-1) {
newNode->name = malloc( strlen(n) + 1 );
strcpy(newNode->name, n);
newNode->age = a;
newNode->next = ptr;
return newNode;
}
duplicate = 1;
return ptr;
}
//function to find age of the passed person
int getAge(Person *ptr, char *name) {
while (ptr) {//while loop to traverse entire linked list elements (All persons one by one)
if(strcmp(ptr->name, name) == 0) //comparing person name in the list with the search key name
return ptr->age; //if found, returning the age of that person
else
ptr = ptr->next; //if not found, check in next node of linked list
}
return -1; // if not found, even after visting all nodes, return -1
}
You can do an insertion sort. Each time you add a new record, you scan through the list to see where it belongs and insert it there. This could be combined with your scan for duplicates.
Person *add(Person *head, char *n, int a) {
char empty[1] = "";
Person sentinel = {0};
sentinel.name = empty;
sentinel.next = head;
Person *p = &sentinel;
while (p) {
int cmp = p->next ? strcmp(n, p->next->name) : -1;
if (cmp == 0) {
printf("Name Already Exists in List! Please retry with another name..\n");
break;
}
if (cmp < 0) {
Person *newNode = malloc( sizeof(Person) );
newNode->name = malloc( strlen(n) + 1 );
strcpy(newNode->name, n);
newNode->age = a;
newNode->next = p->next;
p->next = newNode;
break;
}
p = p->next;
}
return sentinel.next; // a possibly-updated copy of head
}
Insertion sort always compares the new element to the next element (rather than to the current element). This makes dealing with the first element awkward, especially in a list. We get around that with a temporary "sentinel" that we pretend is just before the head of the list.
There are other approaches. You can create the new node at the head of the list and then slide it down until it's in position. If you encounter a duplicate, you remove the new one and patch up the list. Insertion sorts in other data structures typically work from the tail back toward the head, but that won't work with a singly-linked list.
I wrote something similar where I sorted student ID's. You should try doing a swap. Declare a temp variable and used that to swap. The code is something like.
int temp,
first_name,
last_name;
temp = first_name;
first_name = last_name;
last_name = temp;
Hope that gives you an idea!
Edit: What the other person suggested is a good idea as well, an insertion sort.
Your question has already been answered, but I'd like to point out another approach to adding a node. The list is defined by its head node. When iserting elements, the head node may change. (It will change in your current code, which adds elements at the front.) In order to reflect the change, you return the new head:
myList = add(myList, input1, input2);
This is redundant, because you have to specify myList twice. It is also legal to discard the result returned from a function, so there is the possibility of an error. If you pass in a pointer to the head pointer, you will eliminate the redundancy.
add(&myList, input1, input2);
The ´&will indicate thatmyList` may change. And you can now use the return value for something else, for example a pointer to the newly inserted node or null if the name was already there.
Inserting a person at the front (unconditionally) looks like this:
Person *add_front(Person **ptr, const char *name, int age)
{
Person *p = person_new(name, age);
p->next = *ptr;
*ptr = p;
return p;
}
Inserting at the end requires to walk the list first:
Person *add_back(Person **ptr, const char *name, int age)
{
Person *p = person_new(name, age);
while (*ptr) {
ptr = &(*ptr)->next;
}
p->next = *ptr;
*ptr = p;
return p;
}
Note how you do not need to treat the empty list as a special case. Adrian's solution eliminates the special case with a sentinel element. Here, it is eliminated by the pointer itself: ptr points to the list head pointer in the calling function at first. As you walk through the list, it points to the next pointer of the previous node. Updating *ptr updated the pointer that took us to the current node.
The function person_new creates a new node. I find it tidier to make this a separate function, which you could call from other functions:
Person *person_new( const char *name, int age)
{
Person *p = malloc(sizeof(*p));
p->name = malloc(strlen(name) + 1);
strcpy(p->name, name);
p->age = age;
p->next = NULL;
return p;
}
Now the function you want, which inserts the node in alphabetical order, looks like this:
Person *add(Person **ptr, const char *name, int age)
{
while (*ptr && strcmp((*ptr)->name, name) < 0) {
ptr = &(*ptr)->next;
}
if (*ptr == NULL || strcmp((*ptr)->name, name) != 0) {
Person *p = person_new(name, age);
p->next = *ptr;
*ptr = p;
return p;
}
return NULL;
}
When you look up a node by name, you can stop the search short when the current node is alphabetically larger than the name you're looking for:
const Person *find(const Person *ptr, const char *name)
{
while (ptr) {
int cmp = strcmp(ptr->name, name);
if (cmp > 0) break;
if (cmp == 0) return ptr;
ptr = ptr->next;
}
return NULL;
}
You can see the code in action here.

Printing a linked List only prints most recent data entry in C

I'm printing a linked list after each addition to it. The problem is that it only prints the most recently added node. The user is supposed to enter a string which is used to make a node and then that node is added to the list. Here is the code:
int main() {
char userChoice = printMenu();
int setNumber;
while (userChoice != 'q') {
printf("set: ");
scanf("%d", &setNumber);
Node **nodeArray;
nodeArray = malloc(10 * sizeof(Node *));
int i;
for (i = 0; i < 10; i++) {
nodeArray[i] = malloc(sizeof(Node));
}
if (userChoice == 'a')
add(&nodeArray, setNumber);
else
printf("Please enter a valid menu option.");
//printf("%s\n", (nodeArray[setNumber]->next)->data);
userChoice = printMenu();
}
void add(Node ***nodeArray, int setNumber) {
char userString[5];
printf("Please enter some data: ");
scanf("%s", userString);
Node *head = *nodeArray[setNumber]; /* head pointer to first element of array (dummy) */
Node *newNode = malloc(sizeof(Node)); /* new node to be added to array */
strncpy(newNode->data, userString, sizeof(newNode->data)); /* copies string entered by the user to data field of new node */
newNode->next = NULL; /* initializes next field of new node to NULL */
while (head->next)
head = head->next; /* points head to next element in list */
head->next = newNode; /* adds element to list */
head = *nodeArray[setNumber]; /* points head back to start of list */
Node *tmp = head;
printf("List is: ");
while (tmp->next) {
printf("%s", tmp->data);
tmp = tmp->next;
}
}
Just as an example, when I enter "one", it prints out "one". Then when I add "two" it only prints out two instead of printing out "one two". What am I doing wrong?
This *nodeArray[setNumber] means *(nodeArray[setNumber]) but you seem to mean (*nodeArray)[setNumber]. Or better, don't pass &nodeArray to add(), just pass nodeArray. So:
add(nodeArray, setNumber);
...
void add(Node **nodeArray, int setNumber) {
...
Node *head = nodeArray[setNumber];
The problem is that a new block of memory is being created every time you enter into the loop. Cut and paste the code that creates the array outside of the loop and everything should work fine.

Symbol table implementation using hash table in C

I am trying to implement a simple symbol table that stores the strings in a hash table according to their hash values. The hash table in my program is an array of pointers to linked lists. we have 6 linked lists corresponding to each hash value.
The problem is that though the program runs, it replaces the old strings with the new string in each iteration.
my code is..
struct node{
char *string;
struct node *next;
};
struct node *hashtable[6];
int calchash(char *arr);
main()
{
char *line, a='n';
int val, i;
do{
printf("Enter string:\n");
scanf("%s", line);
struct node *current;
struct node *q= (struct node*)malloc(sizeof(struct node));
q->string = line;
q->next = NULL;
val= calchash(line);
if(hashtable[val] == NULL)
{
hashtable[val] = q;
current =q;}
else{
current->next = q;
current = q;
}
printf("Node created\n");
for(i=0; i<6; i++)
{ printf("Hash value %d :\n", i);
if(hashtable[i]==NULL)
{printf("No STRINGS!\n\n");}
else
{struct node *t = hashtable[i];
while(t != NULL)
{printf("%s \n", t->string);
t = t->next;}
printf("\n\n");
}
}
printf("CONTINUE(y/n):\n");
scanf(" %c", &a);
}while(a!='n');
}
int calchash(char *arr)
{int i=0, ascii;
int sum=0;
while(arr[i] != '\0')
{ascii = arr[i];
if(ascii>=48 && ascii<=57)
{
sum+= 2*ascii;}
else
{sum=sum+ ascii;}
i++;
}
return ((sum*17+5)%6);
}
And the output is:
Enter string:
az9
Node created
Hash value 0 :
No STRINGS!
Hash value 1 :
No STRINGS!
Hash value 2 :
az9
Hash value 3 :
No STRINGS!
Hash value 4 :
No STRINGS!
Hash value 5 :
No STRINGS!
CONTINUE(y/n):
y
Enter string:
Az9
Node created
Hash value 0 :
No STRINGS!
Hash value 1 :
No STRINGS!
Hash value 2 :
Az9
Hash value 3 :
No STRINGS!
Hash value 4 :
Az9
Hash value 5 :
No STRINGS!
CONTINUE(y/n):
n
Can someone please tell me what changes are needed so as to retain the previous az9 string under hash value 2???
if(hashtable[val] == NULL) {
hashtable[val] = q;
current =q;
} else {
current->next = q;
current = q;
}
should be replaced with:
q->next = hashtable[val];
hashtable[val] = q;
// no need for current
Also, writing through any uninitialised pointer is UB, please allocate sufficient space first. Anything might happen...
How does this not crash immediately? Neither line nor hashtable are initialized.
You will need to make a copy of the string to go into each hash node, probably with strdup. Currently, all of the nodes point to the same string buffer as line, so when you read a new string into line, all of the nodes will see it. This is why you must duplicate the string for each node. I wonder where the buffer ended up though, since you never initialized line...
Also, what is current? It is local to the loop, and appears unnecessary. You should just chain new nodes onto the head of the bucket, so you don't need to check if the bucket is empty.
The insert also does not check if the string is already present, so you will insert duplicates.

Resources