I'm implementing a generic doubly linked list in C, and I've written functions for forward and backward traversal. Within these functions, I'd like to print the data contained in the list, but since it's generic, I'm having a hard time figuring out how this can be done. Obviously I can't just printf using %d or something because the list could contain any data type. Any suggestions to approach this differently would be appreciated because I've been thinking about it for quite some time and I'm at a loss. Thanks!
You can do a lot of things.
For example, you can store structs that not only contain a void * to your element's data, but also an indication for the possible data type, or even just the format string necessary to printf the object.
You could also think of a struct that contains a void * to your data, and a function pointer that will allow you to convert your data to a string. That is basically minimally emulating C++'s polymorphism in C.
EDIT: as wickstopher pointed out, you just don't get compile-time type safety with this. Mess up the function pointer, and you'll have an unsuitable function working on your data, possibly causing your program to segfault, run over your kitten, burn down your apartment, run away with your youngest child or smoke crack in your kitchen.
You need a tag field in the struct you declared for node. Define an enum for data types as
enum {INT_TYPE, FLOAT_TYPE, DOUBLE_TYPE, CHAR_TYPE} type;
For every data type you need to assign type with corresponding enumeration constant. In printing function you will have to check the value of type and then use the appropriate specifier.
C does not support any sort of runtime type checking, so this is not possible. See runtime determine type for C (similar question).
If you want to support a limited range of data types, you might consider an adding an enum to your node structure, but this won't get you true generic functionality and won't be enforceable at compile time.
Assuming your node has a void * pointer to node contents, among pointers for referring the next and previous elements in the list, provide a function to print a node, such as
void PrintNode (Node_t *node, void (*fprint)(void *));
This function will get the elements of a node, and then call a user-provided function to actually print the contents of a node.
typedef struct stNode {
void *NodeContents;
struct stNode *prev;
struct stNode *next;
} Node_t;
void PrintNode (Node_t *node, void (*print)(void *))
{
if (node && node->NodeContents && print)
print(node->NodeContents);
}
Related
This is a code snippet from qemu.(qemu-5.1.0 include/hw/arm/smmu-common.h)
typedef struct SMMUDevice {
void *smmu;
PCIBus *bus;
int devfn;
IOMMUMemoryRegion iommu;
AddressSpace as;
uint32_t cfg_cache_hits;
uint32_t cfg_cache_misses;
QLIST_ENTRY(SMMUDevice) next;
} SMMUDevice;
I've seen many such codes until now but I am now curious if there is any principle/rule in choosing between
embedding a struct A inside a struct B
embedding a pointer to the struct A inside a struct B
Two things that come to my mind right away is that if a struct A is to be shared by many structs, it is better to use pointer. or if the struct containing the struct(that is, struct B) is to be frequently passed as a function argument, it would be better to use pointer(pointer to struct B as argument, or pointer to A inside struct B and struct B is the argument) because copying the struct to stack would take long time.
I am curious if there are other important rules.
There's no correct answer because it depends on what you want to use them for. Storing a struct inside another struct is generally more efficient, since it gives faster access and better data cache use.
However, it isn't as flexible. If you wish to swap out the whole contents of a big struct for something else, it goes much faster to just swap two pointers than doing a hard copy of all the data. Pointers also enable different forms of allocation - you could have a static storage struct with a pointer at dynamically allocated memory for example.
if a struct A is to be shared by many structs, it is better to use pointer
I don't see how that matters at all. It's just a . vs -> notation by the code using it.
or if the struct containing the struct(that is, struct B) is to be frequently passed as a function argument, it would be better to use pointer
No that's nonsense, you'd always pass the outer struct through a pointer no matter what members it got. Passing it by value doesn't make any sense in either scenario.
This question already has answers here:
Why do linked lists use pointers instead of storing nodes inside of nodes
(11 answers)
Closed 5 years ago.
I could not grasp the reason we create pointers of nodes instead of node structures when we try to implement linked lists as here:
typedef struct node {
int val;
struct node * next;
} node_t;
and
node_t * head = NULL;
head = malloc(sizeof(node_t));
if (head == NULL) {
return 1;
}
head->val = 1;
head->next = NULL;
here, why do we declare nodes such as head as pointers of structures instead of direct structures>
Having head as a pointer allows for things like empty lists (head == NULL) or a simple way to delete elements at the front of the list, by moving the head pointer to another (e.g. second) element in the list. Having head as a structure, these operations would be impossible or at least much less efficient to implement.
The primary reason is that the C language simply won't allow it - a struct type cannot contain an instance of itself. There are two reasons for this:
The struct type is not complete until the closing }, and you cannot create an instance of an incomplete type;
If a struct type could contain an instance of itself, then the instance would be infinitely large (struct foo contains an instance of struct foo, which contains an instance of struct foo, which contains an instance of struct foo, ad infinitum).
You can, however, create pointers to incomplete types (since the size of the pointer doesn't depend on the size of the pointed-to type). So if you want a struct type to contain a member that refers to another instance of the same type, it must be done through a pointer.
why do we declare nodes such as head as pointers of structures instead of direct structures
Declaring head as a pointer allows us to have an empty list, i.e. when head is NULL
Because that's the whole point: a collection of nodes, that are linked like a chain. You can detach and re-attach nodes with ease and poise, because to do so you need only change pointer values. This would be impossible if your type were more like an array. If you want that, use an array.
Besides which, it is impossible for a type to contain an instance of itself. So a node contains a node, which contains a node, which contains a node, which contains a node, and so forth… how can that work?
As someone else has already said, by using pointers we have a convenient way of indicating an empty list or the end of the list by using a NULL pointer.
We could of course modify our nodes to have a flag indicating that it is "the end of the list" (EOL). However, another important reason it that is give the list the ability to easily grow (up to the amount amount of available memory) or shrink dynamically without having to reallocate memory to hold the entire list and copy it every time that it grows or shrinks. It also makes it easier to insert or remove an item.
It would not be a "linked" list if the nodes are not actually linked to each other. It could still be a list of some sort, but it would not be linked.
Compare to the word "link" or hyperlink on the internet. They are also pointers, because almost no site store actual contents of the linked sites.
I have met two ways to implement double linked list:
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
this way is in the FreeBsd queue.h,I want to know why it uses a pointer to pointer le_prev?
struct list_head {
struct list_head *next, *prev;
};
this way is in the linux list.h, it just uses two simple pointers.
What is the difference? Which is better, just use a simple pointer and a pointer to pointer or two simple pointers?
The list is not "doubly-linked", in the sense that the list is not traversable in both directions. The double pointer is given so that the next pointer can be changed quickly.
Also, in c, structure elements are always stored contiguously. So given the address of next pointer, you can calculate the address of the previous node. But this simply adds complexity in the code and requires you to calculate the size of all elements in the structure. It's not worth the trouble. (You should not assume this and say that it's a linked list with 2-side traversal)
Which is better?
Implementation with two simple pointers is generally better. There might be special situations where double-pointer implementation might be better.
I wrote a Linked List ADT for a class I am in originally for a list of ints. Now I am going to use the same list for chars and ints. I know how to rewrite the code for chars to just have basically two List ADTs, one for the ints and the other for the chars.
I don't want to do that however, I want to write it for a generic variable so that in the future I can use this ADT with other code without worrying about types to much.
Initially I went into this with void*, but I am running into an error with it that I am just have a hard time understanding how to fix.
typedef struct NodePtr {
void* data;
struct NodePtr*next;
struct NodePtr*prev;
} NodePtr;
typedef struct ListStruct {
NodePtr* first;
NodePtr* last;
NodePtr* current;
} ListStruct;
Then some other code and fun stuff, and then my insert method to insert at the front of the list:
void insert(listhndl L, void* data)
{
...inserting of the node here.
}
This code throws no error, but then when I run a driver to test it:
ListHndl test = NULL;
test = newList();
insert(test, 1);
I get the error message:
warning: passing argument 2 of 'insert' makes pointer from integer without a cast [enabled by default]
insert(test, 1);
^
error: expected 'void *' but argument is of type 'int'
void insert(ListHndl L, void* data);
I am confused here because how can it throw an error saying it's not the type it expected, if void* is a generic type?
What am I doing wrong?
I saw on here some people recommend using enum and unions for generics instead of void*, but I could not get that to work ether as I don't really understand what to do with them. If somebody wanted to also answer how to do generics with the enums/unions method I would greatly appreciate it.
Your list addition parameter is a void* so naturally you'll be flagged with an implicit conversion warning (or error if you're compiling with -Wall -Werror like you should be). Generic implementations of any node management algorithms isn' a trivial as it may seem, and much has been written/coded on the subject.
In your case, you could dynamically allocate the data being added yourself (i.e. allocate an int and send the resulting address as the void*, or create additional parameterization of your list interface functions and make them smart enough to figure out what to allocate (if anything)
In general, a generic linked list is eventually not going to escape ownership and sizing information if you're planning on using pointers (and all of the samples linked below go to some lengths to accommodate this). An elaborate enough interface capable of constituting this information into a reasonable node architecture that is generic enough is tedious, but that is the price you pay for generics. Performance is likewise a factor, and again, pay the piper. Type information for a list that holds different data types may also be a consideration, in particular if you're inclined in your algorithms to minimize memory management usage.
There are a multitude of generic linked lists sourced all over this grand illusion we call the World Wide Web. Here are a few such implementations:
PseudoMuto Generic Linked List : Assumes all items are the same size, maintains dynamic storage to them via internal memory management.
CMU Generic Linked List: lecture notes on generics development.
Uncredited Generic List (circa 1999): Another fixed size dynamically managed linked list. Not particularly impressive, but at least functional.
Atachil's Generic Linked List: Yet another generic implementation, this one implemented as a double-linked list.
Plenty more where that came from (google and 10 minutes of filtering out junk).
You needn't reinvent the wheel, but if you want to there are plenty of examples out there that can assist you. A challenge would be implementing a list that
Takes user-provided allocation/deallocation functions for any memory management requirements
Uses a union internal to the list_node that supports all the fundamental data types as well as a void* and size_t for blob or string data, and of course, a type-identifier so you know what member is the member.
Provides dynamic ownership semantics (i.e. allows the client to specify a dynamic node pointer is "owned" by the client and to not store a duplicate; just use the provided data pointer.
Bi-directional management (i.e. a double linked list)
Just a few things to consider when embarking on your quest. I wish you the best of luck.
You can make the type of data to be handled more opaque by encapsulating it into a typedef:
typedef struct my_opaque_data {
int item; /* example opaque data */
} DATA;
struct NodePtr {
DATA data;
struct NodePtr *next, *prev;
} NodePtr;
So far, all I have done is replace void * with DATA.
Then you will probably want to declare some basic operations to be implemented elsewhere to handle the type:
extern int data_compare (const DATA *left, const DATA *right); /* return < 0 if left < 0, 0 if equal, etc. */
extern void data_set (DATA *dest, const DATA *src); /* *dest = *src */
extern void data_swap (DATA *d1, DATA *d2); /* exchange *d1 and *d2 */
extern size_t data_size (const DATA *d); /* return size (bytes) of *d */
...
Each of these is almost certainly trivial to implement, but by substituting different data types, even complex data types, in DATA, the source code can remain complete generic and oblivious to what is in DATA. With this one could easily implement list operations, sorting, searching, etc., etc.
I'm implementing a generic Linked List in C
struct Node
{
void* data;
struct Node* next;
};
Is it better to let the user worry about allocating and deallocating what data will point to, or should we do it ourselves? If left to the user they may store stack objects into the list which could cause problems later. I just wanted to know which design is better.
The general rule of thumb is usually: Who allocates a memory - is responsible for freeing it.
In your case, you should take care for the nodes themselves, and the user should be responsible for the data.
It makes sense because:
By taking responsibility on the nodes- it gives you more freedom to change implementation in future versions, without needing to worry about backward compability.
You cannot know how and if to free data - it could be a complex type that needs freeing in inner fields as well, or it could be pointing to a stack allocated space, which will cause an error if trying to free it.
In addition, remember that if a user took an element out of the
list - it does not mean he wants to destroy the data. Maybe the
list is a queue, and the element is currently being processed by
him?
You can also create a function that takes a function pointer and data type to deallocate in your generic link list. User can supply customized code in the function and pass pointer to that function to execute their version of code to free the allocated memory in the complex data type, e.g. nested structures that need to be traversed or a structure that has multiple pointer variables that need to be deallocated as well.
Following is a snippet for this idea.
main.c
int int_data; /* int can be replaced by any other data type */
deallocate_data(user_defined_free, &int_data);
void user_defined_free(void *data)
{
int *i = (int *)data;
/* cast to your data type and
perform deallocation */
}
somewhere in your generic linked list code
void deallocate_data (void (*p)(void *), void *data)
{
/* call user defined function to free their data structure */
p(data);
}