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.
Related
So I'm running into a little problem with my sorting function.
While it's doing what it's supposed to in the second edge case,
the elements get swapped and then swapped back continuously.
The example here happens when Sven(25) and Bob(22) meet.
void sortPerson(person *pers, person* newPerson)
{
if(pers == NULL || pers->next == NULL)
{
printf("List is emtpy");
return;
}
person* tempValue;
person* prev = pers;
person* curr = pers->next;
//person* newValue = pers;
while(prev != NULL && curr != NULL)
{
//first edge case
//adds a new person
if(prev->age < newPerson->age)
{
newPerson->next = prev->next;
prev->next = newPerson;
}
//second edge case
//swapping process when prev age greater than curr age
//forming a decending order of ages
if(prev->age > curr->age)
{
tempValue = prev;
prev = prev->next;
prev->next = tempValue;
printf("\nPerson age: %d\n", tempValue->age);
printf("loop test\n");
printf("%d and %d\n",prev->age, prev->next->age);
}
//third edge case
//if age is the same do nothing
if(prev->age == curr->age)
{
return;
}
prev = prev->next;
curr = curr->next;
}
}
This function returns a new person
person* addPerson( person *newPers ){
return newPers;
}
And here is my main if you want to test it yourself
int main(){
person* person1 = construct_person("Max", 20);
person* person2 = construct_person("Sven", 25);
person* person3 = construct_person("Bob", 22);
person* person4 = construct_person("John", 23);
person* newPerson = construct_person("Markus", 21);
person1->next = person2;
person2->next = person3;
person3->next = person4;
//person4->next = addPerson(person1, newPerson);
//swapPerson(person1);
sortPerson(person1, addPerson(newPerson));
printperson(person1);
free(person1);
free(person2);
free(person3);
free(person4);
free(newPerson);
}
My struct person and constructor
typedef struct person person;
struct person{
char *name;
int age;
person *next;
};
person* construct_person(char *name, int age)
{
person* pers = (person*) malloc (sizeof(person));
pers->name = name;
pers->age = age;
pers->next = NULL;
return pers;
}
I suspect the problem is that my 'prev' struct pointer gets altered throughout the process, but I'd like a second opinion and potential fix.
*Note I'm only freeing it like this because this is testing only and I'm not gonna add more people else I'd done so iteratively.
Some remarks:
The name sortPerson is not well chosen. It should better be named insertPerson
You cannot hope to sort a list with just one iteration and a swap here and there during that iteration. If that were possible, you would have invented the most efficient sorting algorithm. So, this cannot work -- at least not always.
Instead of implementing some sort algorithm (like bubble sort, selection sort, ...), make sure you add all persons using that insertPerson function. That way you can be sure that the list into which a new person is inserted, is already sorted. This way you don't have to swap elements at all. You only have to find the correct insertion point and insert the new person there. So in the main program you should not be fiddling with next pointers. Leave that to this insertPerson function.
When a list is empty or just has one person, you should still insert the new person. It makes no sense to not do it when the list is empty (or just has one element).
The only base case is when you need to insert the person before all other persons. In that case the reference to the first person needs to change, as the new person becomes that first person. One way to make that work, is to make the first parameter a call-by-reference. In other words: pass the address of where the first person pointer is stored, so that this pointer can be changed, and the caller will have access to that change.
I don't see the logic in omitting an insertion when the age of the new person matches the age of a person already in the list. Surely your list should allow Bob and Alice, even when they are both 28.
There are several other points to make...
Here is a working version. I had to make some assumptions about the code that you didn't share, but the principle should be clear even if your code is different:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct person_s {
int age;
char name[100];
struct person_s * next;
} person;
person* construct_person(char* name, int age) {
person* p = malloc(sizeof(person));
strcpy(p->name, name);
p->age = age;
p->next = NULL;
return p;
};
void insertPerson(person** youngest, char* name, int age)
{
person* newPerson = construct_person(name, age);
// Even when list is empty, still add it
if (*youngest == NULL || (*youngest)->age > age) {
newPerson->next = *youngest;
*youngest = newPerson;
return;
}
person* prev = *youngest;
person* curr = prev->next;
// Search insertion point
while (curr != NULL && curr->age < age) {
prev = curr;
curr = curr->next;
}
// Found it:
prev->next = newPerson;
newPerson->next = curr;
}
void printList(person* pers) {
while (pers != NULL) {
printf("%s is %d years old.\n", pers->name, pers->age);
pers = pers->next;
}
}
void freeList(person** pers) {
while (*pers != NULL) {
person* temp = *pers;
*pers = (*pers)->next;
free(temp);
}
}
int main(){
person* personList = NULL;
insertPerson(&personList, "Max", 20);
insertPerson(&personList, "Sven", 25);
insertPerson(&personList, "Bob", 22);
insertPerson(&personList, "John", 23);
insertPerson(&personList, "Markus", 21);
printList(personList);
freeList(&personList);
}
Think is a function to insert new element in the order of name.
I knew how to do it if I use a if to separate condition of inserting at the start and others. But I was asked to merge the if and while into a single while loop.
How could i integrate the insert function into one while loop with pointer to pointer?
person* insert_sorted(person *people, char *name, int age)
{
person *p=NULL;//,*t=NULL,*q=NULL;
person *ptr= people;
person **ptr2ptr=&ptr;
p=malloc(sizeof(person));
if ( p == NULL ){
printf("malloc() failed\n");
return NULL;
}
else {
p->name = name;
p->age = age;
if ( people == NULL ){ // empty list
people = p;
people->next =NULL;
}
else{
*ptr2ptr = ptr;
while( (*ptr2ptr) !=NULL )
{
if ( compare_people(p, people)<=0 ) // insert at the start
break;
else if ( (*ptr2ptr)->next == NULL) //insert at the end
break;
else if ( compare_people(*ptr2ptr, p) <=0 && compare_people( p, (*ptr2ptr)->next)<=0 )//insert at the middle
break;
*ptr2ptr = (*ptr2ptr)->next;
}
//insert at the end
p->next = (*ptr2ptr)->next;
(*ptr2ptr)->next = p;
}
}
eInstead of trying to find the person element in the list which has no successor, try to find the first null pointer. Something like this (untested):
void insert_sorted(person **p, char *name, int age)
{
while (*p) {
p = &(*p)->next;
}
*p = malloc( ... );
/* ... */
}
This kind of problem is usually best solved with a pen an paper and then drawing a couple of boxes and arrows. The idea is that your 'p' pointer no longer points at a specific person but rather at some pointer which points to a person.
There can be a few options.
I would move the if inside the compare_people function provided that you can change it. After all, adding the very first element in a list is like adding a new "top of the list" element (of least of the list). I know this can be seen as "cheating". And it is, indeed!
You can create a "fake" list element which will always be tested to be the first (or the last) of the sorted list (like with an empty name).
So the list won't ever be empty and there won't ever be a "check for an empty list" test. Of course the content of that fake item needs to comply with the semantics of the compare_people function.
At a cost that's slightly higher than the current O(n), O(n*log(n)) actually, you could use a temporary support structure (like an array of pointers) and qsort() from stdlib.h in order to keep the list sorted.
Finally, implement insertion sort which would exploit the fact that the original set is already sorted before inserting the new element.
The function can be written the following way (without testing because I do not know some definitions of the list)
person * insert_sorted( person **people, char *name, int age )
{
person *p = malloc( sizeof( person ) );
if ( p == NULL )
{
printf( "malloc() failed\n" );
}
else
{
p->name = name;
p->age = age;
person *prev = NULL;
person *current = *people;
while ( current && !( compare_people( p, current ) < 0 ) )
{
prev = current;
current = current->next;
}
p->next = current;
if ( prev == NULL ) *people = p;
else prev->next = p;
}
return p;
}
And the function should be called like
insert_sorted( &people, name, age );
^^^^^^^
Without testing:
person* insert_sorted(person** people, char *name, int age) {
person* added = malloc(sizeof(person));
added->name = name;
added->age = age;
added->next = NULL;
person* previous = NULL;
person* current = *people;
while (current && compare_people(current, added) <= 0) {
previous = current;
current = current->next;
}
if (!people) {
*people = added;
} else {
previous->next = added;
added->next = current;
}
return added;
}
The way you use the pointer to pointer doesn't make use of the indirection. You only write (*ptr2ptr) where you would normally have written ´ptr`.
The idea of using a pointer to a node pointer is that by adding one level of indirection, you are able to access and modify the head pointer from the calling function. If you just pass in a node pointer, all changes to that pointer are local to the insert function and will not update the head pointer of your list in the calling function if necessary.
Your function signature should already pass a pointer to a node pointer:
void insert(person **p, const char *name, int age);
and call it like so:
person *head = NULL;
insert(&head, "Betty", 26);
insert(&head, "Ralph", 23);
insert(&head, "Chuck", 19);
insert(&head, "Alice", 42);
insert(&head, "Simon", 34);
When you enter the fuction, p is the address of head in the calling function. As you iterate through the list with
p = &(*p)->next;
*p hold the address of the next pointer of the previous node. p is a "whence" pointer: It holds the address of the pointer that points to the ode you are processing. That means an empty list isn't a special case any longer.
Your function requires to return the new head pointer. It is easy to forget to assign it and it also adds some redundancy to the call. The pointer-to-pointer approach also fixes this.
Here's how your insertion code could look like with a function that takes a pointer to pointer as argument:
struct person {
const char *name;
int age;
person *next;
};
int compare(const person *p1, const person *p2)
{
return strcmp(p1->name, p2->name);
}
person *person_new(const char *name, int age)
{
person *p = malloc(sizeof(*p));
p->name = name;
p->age = age;
p->next = NULL;
return p;
}
void insert(person **p, const char *name, int age)
{
person *pnew = person_new(name, age);
while (*p && compare(*p, pnew) < 0) {
p = &(*p)->next;
}
pnew->next = *p;
*p = pnew;
}
here i found the most useful answer to this question:http://www.mvps.org/user32/linkedlist.html
ptr2ptr = &people;
while ( *ptr2ptr!=NULL && compare_people(*ptr2ptr,p) ) {
ptr2ptr = &(*ptr2ptr)->next;
}
p->next = *ptr2ptr;
*ptr2ptr = p;
I currently have a linked list and need to add data to it that is inputted by the user from the keyboard so i have two structs:
struct CourseInfo {
int courseID;
char courseName[30];
};
typedef struct CourseInfo courseinfo;
struct StudentInfo {
char StudentID[10];
char FirstName[21];
char LastName[26];
int num_course;
courseinfo array[10];
struct StudentInfo *next;
};
So i have a linked list with 3 nodes currently. I then need to call a function and add a node. The node needs to be inserted in the correct place which is that the studentID before it needs to be less than it and the studentID after needs to be greater so the current IDs i have are 111111111, 333333333, and 444444444 and im trying to add 222222222 so it would go in the second spot so my function looks like:
studentinfo *addStudent(studentinfo *data) //returns type studentinfo* now
{
studentinfo *add;
add = malloc(sizeof(studentinfo));
add->next = NULL; //Now its set to NULL to begin
int knt;
printf("%s", "Adding new student:\nStudent ID: ");
scanf("%s", add->StudentID);
printf("%s", "First Name: ");
scanf("%s", add->FirstName);
printf("%s", "Last Name: ");
scanf("%s", add->LastName);
printf("%s", "Number of courses: ");
scanf("%d", &add->num_course);
for(knt = 0; knt < add->num_course; knt++) {
printf("%s", "Course ID: ");
scanf("%d", &add->array[knt].courseID);
printf("%s", "Course Name: ");
scanf("%s", add->array[knt].courseName);
}
if(searchStudentID(data, add->StudentID)) {
puts("immediately inside if");
while(data != NULL) {
puts("Immediately inside while");
if(strcmp(add->StudentID, data->StudentID) < 0) {
puts("inside if");
add->next = data;
data = add;
}
else {
puts("inside first else");
studentinfo *PrevPtr = data;
studentinfo *NPtr = data->next;
while(NPtr != NULL) {
("inside while(NPTR != NULL)");
if(strcmp(add->StudentID, NPtr->StudentID) < 0) {
add->next = PrevPtr;
PrevPtr->next = add;
break;
}
else {
puts("inside a differnet else");
PrevPtr = NPtr;
NPtr = NPtr->next;
}
}
if(PrevPtr->next == NULL) {
puts("inside last if");
add->next = NULL;
PrevPtr->next = add;
}
}
}
}
else {
puts("Found id");
}
return data; //returns data back to call
}
So i added all those puts statement because i wanted to see why the program kept crashing. So the puts statement puts("Inside a different else") is stuck in an infinite loop and keeps printing. The function searchStudentID simply returns 1 if we dont already have the ID and 0 if we already have it. I know that this function works so there is no need to post it.
I think the problem may be in the break; statement because it doesnt exit from the first while loop but only exits from the inner loop but im not positive.The call to this function look like:
list = addStudent(list); //Now the new data is stored in list
Where list is the linked list with 3 nodes
Linked list management is about managing node pointers, not just nodes. You want to do several things to make this considerably easier on yourself:
Separate the input step from the search+insertion step. They don't belong together regardless of how they may seem otherwise. The biggest benefit this brings to you is reducing your list insertion code to what it should be doing (and only what it should be doing): managing the linked list. I've kept yours intact, but you should really be error checking and doing the data reading somewhere else.
Use a pointer to pointer to walk the list. The biggest benefit from this is eliminating the need to special-case head-position insertion. If that is the position a new node will eventually occupy, so be it, but eliminating that special-case further reduces the complexity of the algorithm.
Don't search the list unless you're capable of retaining the search results to be used for insertion logic. It makes little sense to perform an O(N) scan of the linked list to determine if input data is already present, only to search it again to find the position said-data will actually be inserted. Do it once. Find the position where it belongs. If it is already there, do nothing, otherwise, you sit at the precipice of the proper insertion location already.
Finally, don't allocate a new node unless you know you need one. Use an automatic variable that helpfully self-discards if you end up doing nothing.
Putting all of that together gives something like this:
struct CourseInfo {
int courseID;
char courseName[30];
};
typedef struct CourseInfo CourseInfo;
struct StudentInfo {
char StudentID[10];
char FirstName[21];
char LastName[26];
int num_course;
CourseInfo array[10];
struct StudentInfo *next;
};
typedef struct StudentInfo StudentInfo;
StudentInfo *addStudent(StudentInfo *head)
{
StudentInfo **pp = &head, *p = NULL, rec;
int knt;
// TODO: error check your inputs!
printf("%s", "Adding new student:\nStudent ID: ");
scanf("%s", rec.StudentID);
printf("%s", "First Name: ");
scanf("%s", rec.FirstName);
printf("%s", "Last Name: ");
scanf("%s", rec.LastName);
printf("%s", "Number of courses: ");
scanf("%d", &rec.num_course);
for(knt = 0; knt < rec.num_course; knt++) {
printf("%s", "Course ID: ");
scanf("%d", &rec.array[knt].courseID);
printf("%s", "Course Name: ");
scanf("%s", rec.array[knt].courseName);
}
// walk the list pointers, starting with head, looking for
// a node that is equal or greater than the input node
while (*pp && (knt = strcmp((*pp)->StudentID, rec.StudentID)) < 0)
pp = &(*pp)->next;
// leave now if already present
if (*pp && knt == 0)
return head;
// allocate new node
p = malloc(sizeof *p);
if (p == NULL)
{
perror("Failed to allocate new node");
exit(EXIT_FAILURE);
}
// structure copy.
*p = rec;
// link into proper list position.
p->next = *pp;
*pp = p;
// always return the head (which may have updated above)
return head;
}
That's it. As mentioned, I would personally perform the input operation somewhere other than this function, but I leave that to you to consider.
Best of luck.
Since this function updates the list you either need it to be
studentinfo *addStudent(studentifo *data)
and return the updated head value. Or
void addStudent(studentifo **data)
and do
*data = <new thing>
Issues that I see:
You are not setting add->next to NULL.
You are changing data locally.
add->next = data;
data = add;
changes the value of data locally in the function. It does not change the value in the calling function.
You have the check
while(data != NULL)
following the if statement
if(searchStudentID(data, add->StudentID)) {
but I don't see any code to add the new student when searchStudentID(data, add->StudentID) returns false and when data == NULL to start with.
I'm very new to C, so I'm not totally sure what's the matter. I can't figure out how to print more than a single integer value in a function.
add function:
void add(char *name,int id,int copies)
{
/* Pointer to next item */
struct list *newAlbum;
newAlbum = malloc(sizeof(struct list));
strcpy((*newAlbum).name, name); // Set album name
newAlbum->id = id;
newAlbum->copies = copies;
newAlbum->pNext = pFirst;
pFirst = newAlbum;
}
show function:
void show()
{
system("clear");
struct list *current_node;
current_node = pFirst;
while(current_node != NULL)
{
printf("Album #%d \n",current_node->id);
printf("Album Name: %s \n",current_node->name);
printf("Album Copies:%d \n",current_node->copies);
printf("\n");
current_node=current_node->pNext;
}
}
My program prints out the current_node->id as if it were current_node->copies, and current_node->copies is printed out as 134516043, which is obviously, wrong.
I think I must be passing something wrong to the function or something, but I can't figure it out. Any tips?
I call the function add like this:
add(name,id,copies);
The list is as so:
/* THE LIST */
struct list{
char name[52];
int id;
int copies;
int sold;
struct list* pNext;
};
struct list *pFirst = NULL;
I call the function with user input with this piece of code:
printf("Enter the name of the new album. \n");
scanf("%s",&name);
printf("Enter the album id. \n");
scanf("%d",&id);
printf("Enter number of copies. \n");
scanf("%d," &copies);
// Pass data to add()
add(name,id,copies);
Your code that you've shown is OK, as long as you don't pass an album name to add() which is longer than 51 characters. If you do, you'll get very weird output, and possibly a crash.
To guard against this, you should use a length-limited copy - for example:
void add(char *name,int id,int copies)
{
/* Pointer to next item */
struct list *newAlbum;
newAlbum = malloc(sizeof *newAlbum);
if (newAlbum) {
snprintf(newAlbum->name, sizeof newAlbum->name, "%s", name); // Set album name
newAlbum->id = id;
newAlbum->copies = copies;
newAlbum->pNext = pFirst;
pFirst = newAlbum;
}
}
(note that sizeof *newAlbum is a little better than sizeof(struct list), since the former is "obviously correct" when reading the line - it will still be corret if the type of newAlbum is ever changed).
The only thing I can see wrong here is that you don't check the length of name. You should use:
strncpy(newAlbum->name, 52, name);
This will prevent overrunning the name buffer.
I'm quite new to C and I'm trying to implement a binary tree in C which will store a number and a string and then print them off e.g.
1 : Bread
2 : WashingUpLiquid
etc.
The code I have so far is:
#include <stdio.h>
#include <stdlib.h>
#define LENGTH 300
struct node {
int data;
char * definition;
struct node *left;
struct node *right;
};
struct node *node_insert(struct node *p, int value, char * word);
void print_preorder(struct node *p);
int main(void) {
int i = 0;
int d = 0;
char def[LENGTH];
struct node *root = NULL;
for(i = 0; i < 2; i++)
{
printf("Please enter a number: \n");
scanf("%d", &d);
printf("Please enter a definition for this word:\n");
scanf("%s", def);
root = node_insert(root, d, def);
printf("%s\n", def);
}
printf("preorder : ");
print_preorder(root);
printf("\n");
return 0;
}
struct node *node_insert(struct node *p, int value, char * word) {
struct node *tmp_one = NULL;
struct node *tmp_two = NULL;
if(p == NULL) {
p = (struct node *)malloc(sizeof(struct node));
p->data = value;
p->definition = word;
p->left = p->right = NULL;
}
else {
tmp_one = p;
while(tmp_one != NULL) {
tmp_two = tmp_one;
if(tmp_one->data > value)
tmp_one = tmp_one->left;
else
tmp_one = tmp_one->right;
}
if(tmp_two->data > value) {
tmp_two->left = (struct node *)malloc(sizeof(struct node));
tmp_two = tmp_two->left;
tmp_two->data = value;
tmp_two->definition = word;
tmp_two->left = tmp_two->right = NULL;
}
else {
tmp_two->right = (struct node *)malloc(sizeof(struct node));
tmp_two = tmp_two->right;
tmp_two->data = value;
tmp_two->definition = word;
tmp_two->left = tmp_two->right = NULL;
}
}
return(p);
}
void print_preorder(struct node *p) {
if(p != NULL) {
printf("%d : %s\n", p->data, p->definition);
print_preorder(p->left);
print_preorder(p->right);
}
}
At the moment it seems to work for the ints but the description part only prints out for the last one entered. I assume it has something to do with pointers on the char array but I had no luck getting it to work. Any ideas or advice?
You're always doing a scanf into def and then passing that to your insert routine which just saves the pointer to def. So, since all of your entries point to the def buffer, they all point to whatever was the last string you stored in that buffer.
You need to copy your string and place a pointer to the copy into the binary tree node.
The problem is that you're using the same buffer for the string. Notice your struct is holding a pointer to a char, and you are passing the same char array as that pointer each time.
When you call scanf on the buffer, you are changing the data it points to, not the pointer itself.
To fix this, before assigning it over to a struct, you can use strdup. So the lines of code would become
tmp_*->definition = strdup(word);
Keep in mind that the char array returned by strdup must be freed once you are done with it, otherwise you'll have a leak.