Access data stored in struct within Doubly Linked List? - c

I'm not sure how specific I have to be but I'll give the breakdown best I can. I'm taking a typedef struct:
typedef struct {
char name[21];
int life;
} pcb_t;
inputting values for the name & life, then storing it in a doubly linked-list.
the linked-list structs in the header file are:
typedef struct list_node {
void *data;
struct list_node *next;
struct list_node *prev;
} List_node_t;
typedef struct {
List_node_t *head;
List_node_t *tail;
} List_t;
In my main I have the first struct variables initialized as:
char name[BUF_MAX];
int life;
pcb_t *pcb;
The input is all correct and the pcb struct is stored as a new node in the list. I tried to run a simple loop after the initial input to print out the Name & Lifetime values for each of the pcb structs. The loop I used is this:
void *context = NULL;
void *data;
while( List_next_node( &the_list, &context, &data) && (data != NULL))
{
printf("Name: %s\n", (char *)data);
printf("Lifetime: %d\n", (int )data);
}
Where the List_next_node function transverses the list. the_list is the list, context is what keeps track of where we are in the list, and data is the data.
I'm not sure how to access the information I want as my while loop correctly prints out the Name of the pcb struct, but the lifetime is not.

Lists, doubly linked or otherwise, are a complete red herring, here. The issues are (1) accessing struct members, which hopefully is trivial; and (2) doing that when all you have is a void *, which is possibly a little less intuitive.
This is the simplest way:
void *data;
while( List_next_node( &the_list, &context, &data) && (data != NULL))
{
pcb_t * current_data = data;
printf("Name: %s\n", current_data->name);
printf("Lifetime: %d\n", current_data->life);
}
The only reason your current code "works" for printing out the name is because name is the first element of your struct, and so the address of name happens to be the same as the address of the whole struct, so when you cast the address of the struct to char * you get the result you're expecting, even though you're not really getting there the right way.
As ojblass's answer shows, you can do it with a cast and avoid the use of a temporary variable, but I think a temporary variable makes things a lot clearer.

printf("Lifetime: %d\n", ( (pcb_t *) data) ->life);

Related

How do you write generic list without knowing the implementation of structure?

Let's assume there is an employee ADT, such as
//employee.h
typedef struct employee_t employee_t;
employee_t* employee_create(char* company, char* department, char* position);
void employee_free(employee_t* me);
, and client code would be
#include "employee.h"
employee_t* Kevin = employee_create("Facebook", "Marketing", "Sales");
employee_t* John = employee_create("Microsoft", "R&D", "Engineer");
Now client wanted to use list ADT to insert Kevin and John to list for some task.
//list.h
typedef struct list_t list_t;
list_t* list_create(/*might have some arguments*/);
So client code would then be
#include "employee.h"
#include "list.h"
employee_t* Kevin = employee_create("Facebook", "Marketing", "Sales");
employee_t* John = employee_create("Microsoft", "R&D", "Engineer");
list_t* employee = list_create(/*might have some arguments*/);
list_insert(employee, Kevin);
list_insert(employee, John);
employee_free(Kevin);
employee_free(John);
list_print(employee); //Oops! How to print structure that you can't see?
Because employee is encapsulated by opaque pointer, there is no way for list to copy it.
How to write ADT and implementation for list?
The usual way to do this is to have your list structure store the data as a void*. For example, assmuming your list is a singly linked list:
struct list_t
{
void *data;
struct list_t *next;
};
Now list_insert whould be something like this:
list_t *list_insert(list_t *head, void *data)
{
list_t *newHead = (list_t*)malloc(sizeof(list_t));
newHead->data;
newHead->next = head;
return newHead;
}
If you want to hide away the implementation of the struct then you can add methods to extract the data. For example:
void *list_get_data(list_t *head)
{
return head->data;
}
How do you write generic list without knowing the implementation of structure?
Create functions that handle the structure abstractly.
How to write ADT and implementation for list?
list_create(); needs to pass in helper function pointers for the particular object type to perform various tasks abstractly.
A copy function like void *employee_copy(const void *emp) so list_insert(employee, Kevin); knows how to copy Kevin.
A free function like void employee_free(void *emp) so list_uninsert(employee_t) can free the list when destroyed or members removed one-by-one.
A print function int employee_print(void *emp) so list_print(employee_t) knows how to print each member of its list.
Possibly others.
Rather than pass in 3+ function pointers, consider passing in a struct that contains these pointers, then the list only needs the overhead of 1 pointer: list_create(employee_t_function_list)
You are taking your first steps toward re-writing C++
You can use something called intrusive list. This concept is heavily used in Linux kernel.
All you need is to embed the node into the struct and let the generic code operate only on this struct member.
#include <stddef.h>
struct list_node {
struct list_node *next;
};
struct list_head {
struct list_node *first;
};
/* translates pointer to a node to pointer to containing structure
* for each pointer `ptr` to a `struct S` that contain `struct list_node node` member:
* list_entry(&ptr->node, S, node) == ptr
*/
#define list_entry(ptr, type, member) \
(type*)((char*)ptr - offsetof(type, member))
void list_insert(struct list_head *head, struct list_node *node) {
node->next = head->first;
head->first = node;
}
#define LIST_FOREACH(it, head) \
for (struct list_node *it = (head)->first; it; it = it->next)
The interface can be easily extended by other helpers like list_is_empty, list_first, list_remove_first, embed size to struct list_head.
Exemplary usage:
typedef struct {
char *name;
struct list_node node;
} employee_t;
typedef struct {
char *name;
struct list_head employees;
} employer_t;
employer_t company = { .name = "The Company" };
employee_t bob = { .name = "Bob" };
employee_t mark = { .name = "Mark" };
list_insert(&company.employees, &bob.node);
list_insert(&company.employees, &mark.node);
printf("Employees of %s:\n", company.name);
LIST_FOREACH(n, &company.employees) {
employee_t *e = list_entry(n, employee_t, node);
printf("%s\n", e->name);
}
Prints:
Employees of The Company:
Mark
Bob
Note that the list_* interface can easily used for other types as well.
See article for more information about using this concept for double-linked list.
Edit
Note that list_entry invokes a subtle Undefined Behavior.
It is related to performing pointer arithmetics outside of the struct member object but still within a parent object.
Note that any objects can be treated as an array of chars.
This code will work on all major compilers and it very unlikely to ever fail because it would break a lot of existing and heavily used code (like Linux kernel or Git).
This program is strictly conforming if struct node is a first member of the embedding struct because C standard allows safe conversion between any structure and its first member.
To be strictly conforming if node is not a first member,
The issue could be circumvented by forming a pointer to struct list_node not as &bob.node but rather using a pointer arithmetics on a pointer to bob. The result would be:
(struct list_node*)((char*)&bob + offsetof(employee_t, node))
However, this syntax is really nasty, so personally I would go for &bob.node.

Is it possible to write a generic traverse function in C for different list structures so long as they contain the "next" field?

First time asking a question but I did look around Google and stackoverflow to see if someone has asked something similar before. In malloc, recasting and free, it looked like the OP asked something similar for example. But it was more complicated.
I was wondering whether it's possible to create a generic function for a list structure in C that traverses the list given that you know that the different types of structures will always have a "next" field.
For example, given these two list-type structures:
typedef struct _list1 {
int value;
list1 *next;
} list1;
typedef struct _list2 {
int value;
char *string;
list2 *next;
} list2;
Is it possible to create a generic void freeList((void *) list) function or something which looks something like the below? I am aware it's a simple thing to write both free functions for each individual list separately.
void freeList((void *) list) {
// Included this because the structs would have different sizes
// so I thought it would be possible to cast it in order to properly dereference the field.
if (sizeof *list == sizeof list1)
*list = (list1) list;
else if (sizeof *list == sizeof list2)
*list = (list2) list;
if (!list) return;
else {
free(list->next);
free(list);
}
}
So far, my experiments with the code shown above didn't fare well given that gcc would complain about dereferencing a void * pointer.
Making a heterogeneous list can be achieved by the use of a tagged union, or just a tag and casting:
struct list_item {
struct list_item *next;
enum datatype type;
void *contents;
};
or
struct list_item {
struct list_item *next;
enum datatype type;
union {
int some_int;
char some_char;
} contents;
};
Then while traversing the list you just have to verify the type stored in type before using the contents of the element.
This check:
if (sizeof *list == sizeof list1)
*list = (list1) list;
else if (sizeof *list == sizeof list2)
*list = (list2) list;
doesn't work because sizeof is a static construct: its value is defined at compilation time. You're just asking for the sizeof void.
is it possible to create a generic function for a list structure in C that traverses the list given that you know that the different types of structures will always have a "next" field.
Yes, as mentioned before; you must be careful that every structure starts with the "next" field; the two structures in your post should therefore be reordered like this:
typedef struct _list1 {
list1 *next;
int value;
} list1;
typedef struct _list2 {
list2 *next;
int value;
char *string;
} list2;
It is not clean code, because the compiler could reorder (and pad) the fields of the structure, but in general it should work.
Is it possible to create a generic void freeList((void) *list) function or something which looks something like...
This is possible if your structs do not refer malloced memory; or they do, but in a uniform (and known) way (note the first case is a sub-case of this last).
If the structs contain pointers pointing to memory that has to be freed, in fact, while freeing the struct the freeList() function should also free the referenced memory. A few solutions come to my mind:
1 - If all the different structs contain the same "pointers" layout, the routine can free those pointers in a uniform manner, knowing in advance what to do. In such scenario, one can also use pointer fields that are not used by all the structs, but only some.
2 - Every single instance of a struct could contain some helper field describing the pointer's layout. For example, just after the "next" field, another "mempntcnt" field could tell how many pointers (to be freed) follow the "next" field. Or, this "mempntcnt" could be passed as a parameter to freeList().
3 - This problem could be managed by a totally separated mechanism, outside the scope of freeList(). Much depends on the final usage: I mean, for a given (kind of) linked list, first call a routine that frees all the memory referenced by the list itself, then free the list by calling the common freeList(). After all, if different structs are needed, then different routines are used on them...
I hope I've been clear enough...
If you ensure that the next pointer is the first member of the struct then this is possible.
typedef struct list1 {
// next pointer must be first
struct list1 *next;
int value;
} list1;
typedef struct list2 {
// next pointer must be first
struct list2 *next;
int value;
char *string;
} list2;
void freeList(void *list) {
if (list) {
freeList(*(void**)list);
free(list);
}
}

How does the struct of a linked list know the type of next pointer of its own type?

struct node
{
int data;
node* pointerToNextNode;
};
Here pointerToNextNode is the type of struct node, and it is declared inside the struct.
How does the struct know the type of next pointer of its own type - when it itself hasn't been formed yet?
There is no keyword extern used. How does this work?
It doesn't need to know the structure, it's enough to know the type name, namely struct node — and that has already been defined.
Same result you can obtain by forward type declaration:
struct node; // declare the struct not defining it
struct node *pointer; // declare variable
void foo()
{
if(pointer != NULL) // OK, we use the pointer only
if(pointer->x == 0) // invalid use - struct contents unknown yet
return;
}
struct node { // supply a definition
int x;
};
void bar()
{
if(pointer != NULL)
if(pointer->x == 0) // OK - struct contents already known
return;
}
Here pointerToNextNode is the type of struct node
No, it's not. It's of type struct node *
struct node* pointerToNextNode; allocates memory for a pointer variable of type struct node.
It does not allocate memory for struct node, so, till point, it does not need to know about the size and representation of struct node. Only the (data)type name is sufficient.
Also, it's worthy to mention, without a typedef in place, node* pointerToNextNode; should not be valid. It should be written like below
typedef struct node node;
struct node
{
int data;
node* pointerToNextNode;
};
BTW, private: is not C thing, if i'm not wrong.
for me this doesn't compile using CC -- and exactly because of what you said.
you would have to use struct node * to make the compiler aware you want memory for a pointer

Writing to a linked lists with nested structs

I am trying to write a function that can read some info from a file into a node in a doubly linked list. The format for the each nodes data is as follows.
struct(named record)
artist
album
song
genre
songLength(This is another struct that contains mins and secs)
playcount
rating
void load(FILE *file, Node *head)
{
char tempArtist='\0', tempAlbum='\0', tempTitle='\0', tempGenre='\0'
,tempSpace='\0',tempMins='\0',tempSecs='\0';
SongLength *tempLength=NULL;
int tempPlay=0, tempRating=0,test=0;
tempLength = (SongLength*)malloc(sizeof(SongLength));
fscanf(file,"%s",&tempArtist);
fscanf(file,"%s",&tempAlbum);
fscanf(file,"%s",&tempTitle);
fscanf(file,"%s",&tempGenre);
fscanf(file,"%s",&tempMins);
fscanf(file,"%s",&tempSecs);
fscanf(file,"%s",&tempPlay);
fscanf(file,"%s",&tempRating);
fscanf(file,"%s",&tempSpace);
tempLength->mins=tempMins;
tempLength->secs=tempSecs;
head->data->album=tempAlbum; // breaks here
head->data->artist=tempArtist;
head->data->genre=tempGenre;
head->data->song=tempTitle;
head->data->length=tempLength;
head->data->played=tempPlay;
head->data->rating=tempRating;
}
This is my current load function. When attempting to store these values in to nodes data I get an access violation.
Here are my structs for easy reproduction
typedef struct songlength
{
int mins;
int secs;
}SongLength;
typedef struct record
{
char artist;
char album;
char song;
char genre;
struct songlength *length;
int played;
int rating;
}Record;
typedef struct node
{
struct node *pPrev;
struct node *pNext;
struct record *data;
}Node;
makeNode
Node *makeNode(Record *newData)
{
Node *temp = NULL;
temp=(Node*)malloc(sizeof(Node));
temp->data=newData;
temp->pNext=NULL;
return temp;
}
If any confusion arises just let me know!
Also this is my first experience with dynamic memory so be gentle :P
Thanks!
These lines are not right.
fscanf(file,"%s",&tempArtist);
fscanf(file,"%s",&tempAlbum);
fscanf(file,"%s",&tempTitle);
fscanf(file,"%s",&tempGenre);
fscanf(file,"%s",&tempMins);
fscanf(file,"%s",&tempSecs);
fscanf(file,"%s",&tempPlay);
fscanf(file,"%s",&tempRating);
fscanf(file,"%s",&tempSpace);
They will definitely lead to undefined behavior because of the way the variables are defined.
You cannot expect
char c = '\0';
fscanf(file, "%s", &c);
to work. There isn't enough memory at &c to read a string. You need something like:
char s[100]; // Or some size that is large enough to hold the data
// you are about to read.
fscanf(file, "%99s", s); // Make sure that you don't read more than 99
// characters. Leave at least one character
// for the terminating null character.
I hope that gives you enough clues on how to change your variables.
You did not assign memory for the variable tempLength to point to.
Add this before accessing the elements
SongLength *tempLength = malloc(sizeof(struct(SongLength));
EDIT
I'm just giving an overall idea how to allocate and use nested structs for your case
Node *head;
Record *r=malloc(sizeof(struct record));
SongLength *s=malloc(sizeof(struct songlength));
r->length=s;//<----- 1
r->length->mins=10;//Now you can assign values
head=malloc(sizeof(struct node));
head->pPrev=NULL;
head->pNext=NULL;
head->data=r;//<--- The length member inside record is already assigned memory in 1
head->data->artist='c';
head->data->length->mins=10;//assign

How to use a struct in C?

This is code for a linked list in the C programming language.
#include <stdio.h> /* For printf */
#include <stdlib.h> /* For malloc */
typedef struct node {
int data;
struct node *next; /* Pointer to next element in list */
} LLIST;
LLIST *list_add(LLIST **p, int i);
void list_remove(LLIST **p);
LLIST **list_search(LLIST **n, int i);
void list_print(LLIST *n);
The code is not completed, but I think it's enough for my question. Here at the end of struct node "LLIST" is used, and it's also used as a return type in the prototyping of the function list_add. What is going on?
That's a typedef. It's actually doing two things at once. First, it defines a structure:
struct node {
int data;
struct node *next;
}
And then does a typedef:
typedef struct node LLIST;
That means LLIST is a type, just like int or FILE or char, that is a shorthand for struct node, your linked-list node structure. It's not necessary - you could replace LLIST with struct node in all of those spots - but it makes it a bit easier to read, and helps hide the implementation from pesky end-users.
LLIST is just another type name for the struct that has been created. In general, the following format will create a type "NAME" that is a "struct x":
typedef struct x { ... } NAME;
C requires that you reference structs with a "struct" prefix, so it's common to introduce a typedef for less verbose mention.
That is, the declaration of your struct has two parts, and can be rewritten as such:
struct node {
int data;
struct node *next; /* pointer to next element in list */
};
typedef struct node LLIST;
So, LLIST is just another name for struct node (thanks Chris Lutz).
typedef creates a new "type" in your program, so the return value and types of parameters of those functions are just your struct. It is just shorthand for using struct node for the type.
If you were to create a new node, you could do it like this (using the type):
LLIST *node = malloc(sizeof(LLIST));
node->data = 4;
node->next = someOtherItem;
list_add(node, 1)
Also, with the function prototypes in your question, you don't really need the double pointers; since the data in your struct is just an int, you could do something like
LLIST *list_add(int data, int position);
then the list_add function would handle the allocation, copy the int into the struct and add it to the linked list.
Putting it in at a certain position is as simple as changing the next pointer in the node before it to the address of the newly allocated node, and the next pointer in the new node to point at the next one (the one the node before that one was originally pointing at).
Keep in mind that (given the rest of your function prototypes) you will have to keep track of pointers to every node you create in order to delete them all.
I'm not sure I understand how the search function will work. This whole thing could be implemented a lot better. You shouldn't have to provide the location of a node when you create it (what if you specify a higher number than there are nodes?), etc.
LLIST* is a pointer to a structure defined by the LLIST struct.
You should do
LLIST* myList = malloc(sizeof(LLIST)*number_of_elements);
to have some memory allocated for this list. Adding and removing items requires you to reallocate the memory using realloc. I've already written some piece of code for lists (made with arrays).
I might post the code as soon as I'm home, which is currently not the case.

Resources