Accessing attributes of structures stored in linked lists - c

I'm trying to find a specific location in a linked list and then be able to access its attributes. I know how to sort through the linked list but I can not figure out how to access the name attribute of the Locations.
I define my Location * structure as (These locations are stored into the list later):
#ifndef NESW_STRUCT
#define NESW_STRUCT
typedef struct location{
char *name;
char *longer;
char *shorter;
char *tip;
char *north;
char *south;
char *east;
char *west;
char *logic;
int visited;
char *items[20];
} Location;
#endif
My instructor provides us with a module to create a linked list as well as various functions to manipulate the list. The linked list is comprised of Node * which I believe hold the Locations as well as point to the next node in the list.
typedef struct node
{
Location *loc;
struct node *next;
} Node;
So in my game loop I create a global variable 'world' that is my linked list of (I think) Locations:
Node *world;
and
extern Node* world;
In other modules that also access it.
I then run a simple while loop in my main that creates a Location structure and then joins it to the Linked list(excluded from this post), world, using join(location,world) with the following functions my instructor provided, modified by me to work with Locations rather than void objects. I don't initialize world to anything before joining the first location to it, I think I may need to, but since its a core dump and crashes either way, I can't tell if it makes a difference/is necessary:
Node *
newNode(Location *place,Node *next)
{
Node *n = malloc(sizeof(Node));
if (n == 0)
{
fprintf(stderr,"newNode: out of memory!\n");
exit(1);
}
n->loc = place;
n->next = next;
return n;
}
Node *
join(Location *s,Node *rest)
{
return newNode(s,rest);
}
This all works perfectly fine so far and I create my list successfully. However, elsewhere in my program I created a function that maps through the world list and find the location that has a matching name to whichever name I pass to the function, which, logically, works correctly . I created a temp list that is equal to 'world', and then compared the name attribute of the head of the list to the name of the location I was looking for using, strcmp, returning that location if it matches, and setting the list = to the tail of the list if it doesn't.
Head and Tail are defined here, again provided in the module from my instructor:
Location *
head(Node *items)
{
return items->loc;
}
Node *
tail(Node *items)
{
return items->next;
}
If I understand these functions correctly, using head(list) should return a Location right, not a pointer? Then I should be able to just use 'location->name' to access the name? Apparently not though...
To save time of running through all the game logic just get to the part where it needs to compare the names, I tried writing some temporary code similar to how it would be in the mapping function, to test getting a location from the list and then accessing the attributes.
The probably wrong code I'm using to try and test accessing the list is:
Location *test = 0; //creating an empty location, (not sure if it needs to be initialized to 0 before assigning the desired value but I think I remember a mention of that during class)
test = head(world); //I would like to believe this sets test equal to the location of the head of the list world, but I am fairly certain this is where my error occurs because what is getting assigned to test really isn't a location
printf("%s",test->name); //basic print of the name attribute, I know this works logically because I use it elsewhere when dealing with locations not accessed through world, however this is what causes the core dump because I think I'm trying to access a garbage value so to speak
The program compiles with no errors and successfully reads all the the locations based on a debugging print statement I added. Any help, advice, or tips are greatly welcomed. I know people hate on kids that post here because they think they are trying to get their work done for free, but this is a very small part of an immersive project and once I figure this out, the game is essentially done other than content. I'm at a minor roadblock that is a major inhibitor and have tried everything my friend and I could think of and have even just started changing random data types in the Node struct and join/newNode functions as well as the Location struct hoping to either get lucky or figure out a solution through different error messages that occurred but as you can guess, no luck.

Solution by OP.
Because each new item is joined to the front of the list, the very last Node read into (which is the first node read when going through the list because the list is filled backwards) is the null pointer.
Example: Because the join function used stores a pointer to a Location and then points to the next Node in the list, the list is reversed after reading in.
Say you want to read alpha, bravo, charlie, delta epsilon into the list and you read them in that order, the list in memory looks like this with null being the head in the list:
null<-epsilon<-delta<-charlie<-bravo<-alpha
Therefore when I was trying to print with
Node *spot = world;
Location *loc = 0;
loc = head(spot);
printf("%s",loc->name);
I was attempting the print the name for the location which was really just a null pointer and obviously doesn't exist...So a very easy fix was to set spot equal to the tail before actually working with any of the Nodes in the list.
Node *spot = world;
Location *loc = 0;
spot = tail(spot);
loc = head(spot);
printf("%s",loc->name);

Related

Linked-lists in c

I'm fairly new to coding in c and have stumbled upon a problem. The code below is what I have so far.
The trouble I'm running into is the syntax, more specifically on how to loop through the list. I've spent the past week looking at different resources but none explained how to work with a similar struct data type. The way I've attempted to loop through the list results in an error, and I'm having trouble adding the job in the right place since the list is in increasing order - based on the reference number.
The other problem I've run into is within function 5. I'm not entirely too sure of how to return both an integer and character value through a char pointer, unless you return just the entire job_node. As of now, I have casted the value of the node to be a char.
Since I haven't coded much in c - any other suggestions on how to make the code more efficient without changing the values of the functions would be extremely helpful.
A linked list is a container of nodes, and each node has some data payload. Often each data record has a key, a value used to compare two records. In students' assignments in general the data is a simple letter or a number. In your case is a pointer to a "job post".
Below I will show you an alternative way to write his. But first...
It is important to note that the list is a container of nodes, not a list of job posts. A list is not a node, a node is not a list, and the data is not a node.
Mixing these things like you did in your program leads only to problems: you will not be able to use the list to contain other data, for example. You will have trouble changing the implementation of the list without changing the data. You will need to change the list functions if you change a field in the job post. Only problems.
example
bool job_insert(int refNum, char job_name[], struct job_list * list);
should be like
int insert_l( Job* job_post ,List* the_list ,int(*compare_function)(void*,void*));
Why?
Using the former way you can use just a pointer to Job --- in general you should use a void* so you can use anything inside the data record without the need to change the arguments list.
this way if you add a field to Job or change perhaps the reference number to a char[] to add a check-digit you do not need to change a thing here
compare_function() compare two Job and return -1 if the 1st is greater, 1 if the 2nd is greater, or 0. This way you can change the sorting criteria at any time, just writing a new 10-line function, and have records inserted by new criteria. C qsort() uses this, C++ sort() uses this, Borland Turbo Pascal used this in the 80;s. It is simple and flexible.
the function can be as simples as
int compare_reference(void* a, void* b)
{
int first = ((Job*) a)->reference_number;
int second = ((Job*) b)->reference_number;
if (first < second ) return 1;
if (first > second ) return -1;
return 0;
};
and used like this
int res = insert_l( one_job, new_jobs, compare_reference );
and if res is 0 one_job was inserted in new_jobs list at the correct position. It is also easier to read: insert record into list according to criteria.
And if at any time you need to use descending order or sort by another criteria you just need to pass a new function. This means that you can use the same List-related functions to contain any data. This is the reason to use only void pointers to the data element also...
Back to the program
This could be a test file to drive your tests:
1 engineer
2 magician
3 astronaut
4 actress
5 truck driver
6 writer
The structures
typedef struct
{
int reference_number;
char* job_name;
} Job;
typedef struct node_
{
Job* info; // pointer to data record
struct node_ *next;
struct node_ *prev;
} Node;
typedef struct
{
unsigned size; // actual size
unsigned capacity; // limit in # of nodes
Node* head;
Node* tail;
} List;
And you have a List that is a collection of Node. Each Node has a pointer info that point to a Job, that is the data record. Inside a Job you have the reference number and the job's name. reference_number is called key because it is the field used to compare Job records.
The List has no reference to Job, only to Node. Node has no data, just a pointer. So to use List to contain songs or cars or books like the next assignment in line is easy ;)
the functions
List* create_l(unsigned); // return new List
List* delete_l(List*); // erase one
int insert_l( Job*,List*,int(*)(void*,void*)); // insert based on function
List* merge_l(List*,List*,int(*)(void*,void*) ); // merge 2 lists into a 3rd
int remove_by_ref_l(int,List*); // delete a record
char* search_job_l(int,List*); // search for job description for a reference#
I believe that now you see some added convenience:
insert() receives just a pointer to a Job and to a List, and the name of a function to compare 2.
create() returns a pointer to a new List with the limit set to a max # of nodes
remove() gets a reference number and if it is found in List removes the record and returns 0
search() works the same way
I suggest you write this way. If you need help I can provide more code. I had no time today to write a full example.

Creating Linked List of Locations in C

FIX: I wasn't saving the locations back into world so I was just leaking the information. Credit to Skeeto.
while(!feof(fp)){
loc = readLocation(fp);
join(loc,world);
}
should actually be
while(!feof(fp)){
loc = readLocation(fp);
world = join(loc,world);
}
EDIT: As mentioned in the comments, yes I am a student, but I'm not looking for someone to do my work for me. I'm simply trying to locate possible logic errors because if I could fill this list properly, I can finish my project very easily. This is just a small portion of a very immersive project, helping with this will only allow me to continue the project, not complete it and turn it in. I only provided so much detail because 1) I've never posted here before so didn't know any better, and 2) wanted the reader to understand the workings of this in order to aid them in assisting me. Also, with any concerns as to skype, if that ended up being where successful help was given, I would provide the fix above this 'edit' as well as crediting the stackoverflow user for helping.
TLDR: Yes I'm a student, No im not trying to have someone do my project. This is a very small portion and will only allow me to continue, not complete. If help was given via skype I would update this post with the fix as well as credit the helper.
Hello and thank you for any help in advance.
I am trying to create a linked list that holds objects of type Location *.
I have Location defined as
typedef struct location{
char *name;
char *longer;
char *shorter;
char *north;
char *south;
char *east;
char *west;
char *logic;
int visited;
char *items[20];
} Location;
Furthermore I can succesfully read in all the values for the location and display all attributes so that is not an issue.
In the 'engine' of my game (the main), i attempt to read all the locations into a list as seen in the following (I'm certain readLocation works correctly because I threw a print statement into the loop printing the name of the locations using the loc variable)
world = 0;
FILE *fp = fopen("world.in","r");
char *garb = readToken(fp);
free(garb); //garbage token at begging of world.in just to check file exists
int count = 0; //used later, ignore for now
while(!feof(fp)){
loc = readLocation(fp);
join(loc,world);
}
world is global variable declared as Node * and initialized to 0 (I think i need to do that but am not sure)
In olist.h I create the node structure as
typedef struct node
{
Location *place;
struct node *next;
} Node;
and in olist.c this is how i construct the Node as well as join the nodes
//place is the attribute of the Node that holds the location and next points to the next Node in the list
Node *newNode(Location *loc,Node *next)
{
Node *n = malloc(sizeof(Node));
if (n == 0)
{
fprintf(stderr,"newNode: out of memory!\n");
exit(1);
}
n->place = loc;
n->next = next;
return n;
}
//s is the location i wish to join to the list and rest is list I'm joining to
Node *join(void *s,Node *rest)
{
return newNode(s,rest);
}
Unfortunately, after successfully reading in all locations, world is still an empty list. Thanks for any help and I will be happy to provide further information via this forum or skype: F3V3Rz_MoDz (its a very old name)
If I'm not mistaken, your problem is in the following line of code :
n->next = next;
The head of your linked list is world, but your newNode() function keeps sending world to the back of the list (n->next = next places next at the back). What you want here is to append at the end of your linked list.
Here is an example of code that you can use to do this :
Node *lastNode = next;
while (lastNode->next != NULL)
lastNode = lastNode->next;
lastNode->next = n;
Basically, you iterate through your linked list until you get to the end, then you append the newly created node.
EDIT :
The problem you were having is that the world variable is in the end of your linked list. Every time you call join() you are pushing world at the back of the list. Here's a representation of your list :
Before join() :
world -> null
After join() :
newnode -> world -> null
Therefore, everytime you try to iterate through the list, world does not see the newly created nodes that are before him.
My solution does the following :
world -> newnode -> null
Which basically keeps your world variable in front. So you don't have to do world = join(loc, world)

Why create heap when creating a linked list when we can simply do this?

I'm studying linked lists from this lesson.
The writer (and all other coders on every single tutorial) goes through creating node type pointer variables, then allocates memory to them using typecasting and malloc. It seems kinda unnecessary to me (Offourse I know I'm missing something), why can't we implement the same using this?
struct node
{
int data;
struct node *next;
};
int main()
{
struct node head;
struct node second;
struct node third;
head.data = 1;
head.next = &second;
second.data = 2;
second.next = &third;
third.data = 3;
third.next = NULL;
getchar();
return 0;
}
I've created nodes and the next pointers points towards the addresses of the next nodes...
Let's say you create a variable of type node called my_node:
struct node my_node;
You can access its members as my_node.data and my_node.next because it is not a pointer. Your code, however, will only be able to create 3 nodes. Let's say you have a loop that asks the user for a number and stores that number in the linked list, stopping only when the user types in 0. You don't know when the user will type in 0, so you have to have a way of creating variables while the program is running. "Creating a variable" at runtime is called dynamic memory allocation and is done by calling malloc, which always returns a pointer. Don't forget to free the dynamically allocated data after it is no longer needed, to do so call the free function with the pointer returned by malloc. The tutorial you mentioned is just explaining the fundamental concepts of linked lists, in an actual program you're not going to limit yourself to a fixed number of nodes but will instead make the linked list resizable depending on information you only have at runtime (unless a fixed-sized linked list is all you need).
Edit:
"Creating a variable at runtime" was just a highly simplified way of explaining the need for pointers. When you call malloc, it allocates memory on the heap and gives you an address, which you must store in a pointer.
int var = 5;
int * ptr = &var;
In this case, ptr is a variable (it was declared in all its glory) that holds the address of another variable, and so it is called a pointer. Now consider an excerpt from the tutorial you mentioned:
struct node* head = NULL;
head = (struct node*)malloc(sizeof(struct node));
In this case, the variable head will point to data allocated on the heap at runtime.
If you keep allocating nodes on the heap and assigning the returned address to the next member of the last node in the linked list, you will be able to iterate over the linked list simply by writing pointer_to_node = pointer_to_node->next. Example:
struct node * my_node = head; // my_node points to the first node in the linked list
while (true)
{
printf("%d\n", my_node->data); // print the data of the node we're iterating over
my_node = my_node->next; // advance the my_node pointer to the next node
if (my_node->next == NULL) // let's assume that the 'next' member of the last node is always set to NULL
{
printf("%d\n", my_node->data);
break;
}
}
You can, of course, insert an element into any position of the linked list, not just at the end as I mentioned above. Note though that the only node you ever have a name for is head, all the others are accessed through pointers because you can't possibly name all nodes your program will ever have a hold of.
When you declare 'struct node xyz;' in a function, it exists only so long as that function exists. If you add it to a linked list and then exit the function, that object no longer exists, but the linked list still has a reference to it. On the other hand, if you allocate it from the heap and add it to the linked list, it will still exist until it is removed from the linked list and deleted.
This mechanism allows an arbitrary number of nodes to be created at various times throughout your program and inserted into the linked list. The method you show above only allows a fixed number of specific items to be placed in the list for a short duration. You can do that, but it serves little purpose, since you could have just accessed the items directly outside the list.
Of course you can do like that. but how far ? how many nodes are you going to create ? We use linkedlists when we don't know how many entries we need when we create the list. So how can you create nodes ? How much ?
That's why we use malloc() (or new nodes).
But what if you had a file containing an unknown number of entries, and you needed to iterate over them, adding each one to the linked list? Think about how you might do that without malloc.
You would have a loop, and in each iteration you need to create a completely new "instance" of a node, different to all the other nodes. If you just had a bunch of locals, each loop iteration they would still be the same locals.
Your code and approach is correct as long as you know the number of nodes that you need in advance. In many cases, though, the number of nodes depends on user input and is not known in advance.
You definitely have to decide between C and C++, because typecasting and malloc belong in C only. Your C++ linked list code won't be doing typecasting nor using malloc precisely because it's not C code, but C++ code.
Say you are writing an application such as a text editor. The writer of the application has no idea how big a file a user in the future may want to edit.
Making the editor always use a large amount of memory is not helpful in multi-tasking environments, especially one with a large number of users.
With malloc() an editing application can take additional amounts of memory from the heap as required, with different processes using different amounts of memory, without large amounts of memory being wasted.
You can, and you can exploit this technique to create cute code like this, to use the stack as a malloc in a way:
The code below should be safe enough assuming there are no tail optimizations enabled.
#include <stdio.h>
typedef struct node_t {
struct node_t *next;
int cur;
int n;
} node_t;
void factorial(node_t *state, void (*then)(node_t *))
{
node_t tmp;
if (state->n <= 1) {
then(state);
} else {
tmp.next = state;
tmp.cur = state->n * state->cur;
tmp.n = state->n - 1;
printf("down: %x %d %d.\n", tmp);
factorial(&tmp, then);
printf("up: %x %d %d.\n", tmp);
}
}
void andThen(node_t *result)
{
while (result != (node_t *)0) {
printf("printing: %x %d %d.\n", *result);
result = result->next;
}
}
int main(int argc, char **argv)
{
node_t initial_state;
node_t *result_state;
initial_state.next = (node_t *)0;
initial_state.n = 6; // factorial of
initial_state.cur = 1; // identity for factorial
factorial(&initial_state, andThen);
}
result:
$ ./fact
down: 28ff34 6 5.
down: 28ff04 30 4.
down: 28fed4 120 3.
down: 28fea4 360 2.
down: 28fe74 720 1.
printing: 28fe74 720 1.
printing: 28fea4 360 2.
printing: 28fed4 120 3.
printing: 28ff04 30 4.
printing: 28ff34 6 5.
printing: 0 1 6.
up: 28fe74 720 1.
up: 28fea4 360 2.
up: 28fed4 120 3.
up: 28ff04 30 4.
up: 28ff34 6 5.
factorial works differently than usual because we can't return the result to caller because the caller will invalidate it with any single stack operation. a single function call will destroy the result, so instead, we must pass it to another function that will have its own frame on top of the current result, which will not invalidate the arbitrary number of stack frames it's sitting on top of that hold our nodes.
I imagine there are many ways for this to break other than tail call optimizations, but it's really elegant when it doesn't, because the links are guaranteed to be fairly cache local, since they are fairly close to each other, and there is no malloc/free needed for arbitrary sized consecutive allocations, since everything is cleaned as soon as returns happen.
Lets think you are making an Application like CHROME web browser, then you wanna create link between tabs created by user at run time which can only possible if you use Dynamic Memory Allocation.
That's why we use new, malloc() etc to apply dynamic memory allocation.
☺:).

fast random acces to linked list nodes

I have a singly linked list which can have 10000<< nodes at any given time.
Now in the interface I need to print these in order and a user can acces a single node and perform operations on that node. Obviously if the user chooses a very high number on the node count it will have to go over thousands of node before being able to acces the desired node.
My current fix "translates" the linked list to an array, since my code is multithreaded my linked list can grow at any given time. But by code design never shrink.
Here is code I use to translate linked list to array.
unsigned int i=0;
unsigned int LL_arr_bufsize=128;
my_ll **LL_arr;
my_ll *temp;
LL_arr = malloc(LL_arr_bufsize * sizeof(my_ll *));
// err check mem alooc
temp = l_list->next;
while (temp != NULL) {
LL_arr[i] = temp;
temp = temp->next;
if (++i == LL_arr_bufsize) {
LL_arr_bufsize = LL_arr_bufsize * 2;
LL_arr = realloc(LL_arr, LL_arr_bufsize * sizeof(my_ll *));
// err check mem alloc
}
}
What am I basically wondering if there is a better way to acces any given node without incuring the overhead of traversing the entire list before a given node can be accessed...
I will probably get down voted because I literally just thought of this idea and it might have some flaws. Here it goes.
What if you do a two dimensional node stack. Here me out.
NodeList - holds an array of 10 nodes and it's own index. ( you can experiment with bigger values)
What happens is that NodeList is a regular link list that you can de-queue and queue again. But you can get still some of that constant time look-upness that you are looking for. This is done with a clever search function that goes goes through the link list normally however, once it goes to the location of where your particular node is being held in the list you get that constant time look up from the array it stores.
I can probably clarify more of this concept if you want but I think you can get a good picture of what I'm going for with the description.

Filling a singly linked list nodes

So for my class in C, we are told to create an array of Contacts (telephone numbers and names) with nodes containing the address of the next Contact in the list.
I'm new to pointers and I get the concept of them, but they are confusing. I will comment my thought process and what I think I am doing. We we're provided a lot of this code. I only implemented the Contact structure and disaster of words that form the addLast function.
typedef struct Contact
{
int *Next;
char *Name;
char *Tel;
} Contact;
Contact *start=NULL; //This start variable dictates the first node in the list.
void builtList(Contact*);
void addLast(Contact*);
void printList();
int main()
{
Contact contacts[7]=
{
{NULL,"Dennis","0203/123456"},
{NULL,"Chantal","0177/3123345"},
{NULL,"Robert","0163/9295986"},
{NULL,"Bjoern","040 - 123232345"},
{NULL,"Andreas","+49 178 11903123"},
{NULL,"Jenny","+41 119 34544345"},
{NULL,"Zeuss","0162-123-4531698"},
};
builtList(contacts);
printList();
return 0;
}
void builtList(Contact contacts[]) //This function was written for us.
//I would prefer to modify this as little as possible.
{
Contact *ptr=NULL;
int i;
for(i=0; i<=6; i++)
{
ptr=&contacts[i];
addLast(ptr);
}
}
void addLast(Contact *AddressOfCurrent)
{
/* This is the function I am struggling with. We are told this function is to
"add a Contact element to the end of the singly linked list". From what I understand,
It obtains the ADDRESS of the next node and places it into the current node. Thereby
establishing a link between two elements in the list. My friend did this without
importing the contacts[] array and just using a '*temp' array. This confuses me
greatly. How do I establish links between elements if I don't have the elements
with me? */
if (start == NULL)
{
start = AddressOfCurrent; //I can figure out how to establish the start.
} else
{
?????????
}
}
To clarify, here is my task;
Your task is to implement a Contact List that uses a singly linked
list.
In computer science, a linked list (or more clearly, "singly linked
list") is a data structure that consists of a sequence of nodes each
of which contains a reference (i.e., a link) to the next node in the
sequence.
Realise the following structures/Variables
Create a structure called Contact which has the following variables
Contact *next (pointer to the next element in the List) char *name
(used to store the contact's name) char tel (used to store the
contacts telephone number) Create a global Contact variable called
"start" which points to the start element of the singly linked list.
Realise the following functions (for prototypes see linked source
code):
addLast(Contact*): add a Contact element to the end of the singly
linked list (a few other unrelated functions)
For all listed operations youe have to write a new C function each.
The only actual question I see in your post is this one:
My friend did this without
importing the contacts[] array and just using a '*temp' array. This confuses me
greatly. How do I establish links between elements if I don't have the elements
with me?
Since we don't have your friends code at hand, it's hard to evaluate, so let's forget that. Without adding parameters to the addUser() function, you'd need a global, which you have declared:
Contact *start=NULL;
So that's accessible everywhere. And you are on the right track in addContact():
if (!start) {
But if start isn't NULL, then you need to walk the list to the end via the Next pointer, implying another necessity for the addContact() function:
AddressOfCurrent->Next = NULL;
This means each added node has a NULL next pointer. When another node is added to that one, then you need to the previous last node Next to non-null:
???->Next = AddressOfCurrent;
??? here is the node at the end of the current list you'll find by walking it. It's the only one where Next == NULL ;)

Resources