malloc and pointer in a struct - c

I have the following C code:
typedef struct DListNode_ {
void *data;
struct DListNode_ *prev;
struct DListNode_ *next;
} DListNode;
typedef struct DList_ {
int size;
DListNode *tail;
DListNode *head;
} DList;
void insert(DList * list, DListNode * element, int data) {
DListNode * new_element = (DListNode *)malloc(sizeof(DListNode));
new_element->data = &data;
if (list->head==NULL) {
list->head=list->tail=new_element;
list->size++;
return;
}
if(element == NULL) {
// handle size==0?
new_element->next=list->head;
list->head->prev=new_element;
list->head=new_element;
list->size++;
} else {
printf("Not yet implemented!\n");
}
}
void printNodes(DList *list) {
DListNode * pointer = list->head;
if (pointer!=NULL) {
int v= *((int*)pointer->data);
printf("Node has value: %d\n", v);
while (pointer->next != NULL) {
v = *((int*)pointer->data);
printf("Node has value: %d\n", v);
pointer=pointer->next;
}
}
}
int main(int argc, const char * argv[])
{
int e0 = 23;
int e1 = 7;
int e2 = 11;
DList *list = (DList *)malloc(sizeof(DList));
initList(list);
assert(count(list)==0);
insert(list, NULL, e0);
assert(count(list)==1);
insert(list,NULL, e1);
assert(count(list)==2);
insert(list,NULL, e2);
assert(count(list)==3);
printNodes(list);
return 0;
}
I have a few problems:
does DListNode * new_element = (DListNode *)malloc(sizeof(DListNode)); also allocate space for the, data, prev, next pointer or do I manually need to call malloc on each of those pointers?
When I print the content of the data pointer in each node they all have the value 3 even though I insert 23, 7 and 11 and set the data pointer to the address of the int: ** new_element->data = &data;**.
(Introductionary textbooks on C have been ordered)
EDIT:
insert now takes a void pointer to the data:
// Insert data as the new head
void insert(DList *list, DListNode *element, void *data) {
DListNode *new_element = malloc(sizeof(DListNode));
new_element->data = data;
if (list->head==NULL) {
list->head=list->tail=new_element;
list->size++;
return;
}
if(element == NULL) {
new_element->next=list->head;
list->head->prev=new_element;
list->head=new_element;
list->size++;
} else {
printf("Not yet implemented!\n");
}
}
In main I do:
int main(int argc, const char * argv[])
{
int i0=7;
int *ip0 = malloc(sizeof(int));
ip0 = &i0;
int i1=8;
int *ip1 = malloc(sizeof(int));
ip1 = &i1;
int *ip2 = malloc(sizeof(int));
int i2=44;
ip2 = &i2;
DList *list = malloc(sizeof(DList));
initList(list);
// create some nodes
assert(count(list)==0);
insert(list, NULL, ip0);
assert(count(list)==1);
insert(list,NULL, ip1);
assert(count(list)==2);
insert(list,NULL, ip2);
assert(count(list)==3);
printNodes(list);
return 0;
}
which outputs:
Node has value: 44
Node has value: 44
Node has value: 8
but it should be:
Node has value: 44
Node has value: 8
Node has value: 7

malloc(sizeof(DListNode)) allocates space for exactly one DListNode, which by definition consists of a void* and two DListNode pointers. It does not initialize those pointers, though.
You're assigning the address of the data argument to insert. That's a pointer to a temporary which is invalidated once insert returns. The behavior of the program is undefined. The easy solution is to just replace void *data by int data.

You need to manually set those pointers to where they point with malloc. Without it, they will point to a space that isn't of size DListNode.
Don't make the data a pointer. Just make the data an int (it gets auto allocated) and then just set data = data (the data that is passed into insert).

Related

Code is printing pointer address (I think) instead of value?

I have a list defined as
typedef struct node {
Voo *voo;
ListaReservas nodeReservas; /* Ignore this */
struct node *next;
} *Node;
I created some functions to help me add or remove nodes from the list like:
/* creates a node */
Node criaNode(Voo v) {
Node new = (Node)malloc(sizeof(struct node));
new->voo = &v;
/* I had new->voo = v; but vscode told me it was wrong so i changed it to &v */
new->next = NULL;
return new;
}
Voo is defined as:
typedef struct {
int dia;
int mes;
int ano;
} Data;
typedef struct {
int horas;
int minutos;
} Tempo;
typedef struct {
char codigo[LEN_CODIGO + 1];
char partidaID[LEN_ID + 1];
char chegadaID[LEN_ID + 1];
Data datapartida;
Tempo horapartida;
Tempo duracao;
Data datachegada;
Tempo horachegada;
int capacidade;
} Voo;
Now I wanted to iterate through the list and print its values as such
Voo *v;
for (n = headVoos; n != NULL; n = n->next) {
v = n->voo;
printf("%s %s %s %.2d-%.2d-%d %.2d:%.2d\n",
v->codigo, v->partidaID, v->chegadaID,
v->datapartida.dia, v->datapartida.mes, v->datapartida.ano,
v->horapartida.horas, v->horapartida.minutos);
}
The program is not printing correctly. For example where it should appear
AA1 AAA AAD 16-03-2022 14:50
its appearing instead
� 146187376-32765--1940381952 40355300:50
What's causing this and how can I avoid it in the future?
EDIT
After replacing in the struct node the Voo *voo definition by Voo voo, I am now getting an error in one of the auxiliary functions:
/* deletes node */
Node eliminaNode(Node head, Voo v)
{
Node n, prev;
for (n = head, prev = NULL; n != NULL; prev = n, n = n->next)
{
if (n->voo == v) /* expression must have arithmetic or pointer error */
{
if (n == head)
head = n->next;
else
prev->next = n->next;
free(n->next);
free(n);
break;
}
}
return head;
}
In criaNode you're taking the address of the parameter v and returning it from the function via a pointer to dynamic memory. That address is no longer valid after the function returns. Subsequently dereferencing that invalid address then triggers undefined behavior.
It probably makes more sense for struct node to contain a Voo directly instead of a pointer to one. So change the member to a non-pointer:
Voo voo;
And assign the parameter directly:
new->voo = v;
There are multiple problems here:
there seems to be a confusion between structures and pointers to structures. In C, you must understand the difference between manipulating objects (allocating as local objects or from the head, passing as arguments or returning as values) and pointers to objects, which are a more idiomatic as arguments to functions and allow functions to modify the object they point to.
the confusion is amplified by a very error prone construction: hiding pointers behind typedefs. Do not do that, define object types for the actual structure, using the same or a different name as the struct tag, and make all pointers explicit with the * syntax.
you pass an actual Voo object as an argument and allocate a list node using the address of this argument. This is incorrect because the argument will be discarded as soon as the function returns, makeing the list point to invalid memory and explaining the weird output you observe.
Node eliminaNode(Node head, Voo v) should take a pointer to the head node and return a success indicator. It should take a Voo * argument and it should not free(n->next) because the next node is still in use after the removal.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#define LEN_CODIGO 30
#define LEN_ID 30
typedef struct Data {
int dia;
int mes;
int ano;
} Data;
typedef struct Tempo {
int horas;
int minutos;
} Tempo;
typedef struct Voo {
char codigo[LEN_CODIGO+ 1];
char partidaID[LEN_ID + 1];
char chegadaID[LEN_ID + 1];
Data datapartida;
Tempo horapartida;
Tempo duracao;
Data datachegada;
Tempo horachegada;
int capacidade;
} Voo;
typedef struct Node {
struct Voo *voo;
//ListaReservas nodeReservas; /* Ignore this */
struct Node *next;
} Node;
/* creates a node */
Node *criaNode(Voo *v) {
/* allocation with calloc is safer as the object will be initialized to 0 */
Node *nodep = calloc(1, sizeof(*new));
if (nodep) {
nodep->voo = v;
nodep->next = NULL;
}
return nodep;
}
/* deletes node */
int eliminaNode(Node **head, Voo *v) {
for (Node *n = *head, *prev = NULL; n != NULL; prev = n, n = n->next) {
if (n->voo == v) {
if (n == *head)
*head = n->next;
else
prev->next = n->next;
free(n);
return 1; /* article was found and freed */
}
}
return 0; /* article was not found */
}
void printList(const Node *head) {
for (const Node *n = head; n != NULL; n = n->next) {
const Voo *v = n->voo;
printf("%s %s %s %.2d-%.2d-%.2d %.2d:%.2d\n",
v->codigo, v->partidaID, v->chegadaID,
v->datapartida.dia, v->datapartida.mes, v->datapartida.ano,
v->horapartida.horas, v->horapartida.minutos);
}
}

Generric search function at generic link list

I have generic link list in C that know how to push struct to list.
The problem is the I can't implement generic search in those link list:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
struct Node
{
void *data;
struct Node *next;
};
void push(struct Node** head_ref, void *new_data, size_t data_size)
{
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = malloc(data_size);
new_node->next = (*head_ref);
int i;
for (i=0; i<data_size; i++)
*(char *)(new_node->data + i) = *(char *)(new_data + i);
(*head_ref) = new_node;
}
struct A
{
int a1;
long a2;
};
struct B
{
long b1;
int b2;
};
void find_a1_in_a_list (int desire_a1 , struct Node *a_list)
{
struct A *a;
while(NULL != a_list)
{
a = (struct A*) a_list->data;
if(a->a1 == desire_a1)
printf("found!\n");
a_list = a_list->next;
}
}
void find_b1_in_b_list (long desire_b1 , struct Node *b_list)
{
struct B *b;
while(NULL != b_list)
{
b = (struct B*) b_list->data;
if(b->b1 == desire_b1)
printf("found!\n");
b_list = b_list->next;
}
}
void find_generic (void* desire_value,int off,struct Node *list)
{
while(NULL != list)
{
void* check_value_void = list->data + off;
int check_value_cast = *(int *) check_value_void; //How to know if cast to int or long ?????
if(check_value_cast == *(int *)desire_value) //How to know if cast to int or long ?????
printf("found generic!\n");
list = list->next;
}
}
void main()
{
struct Node *a_list = NULL;
struct A a;
a.a1=1;
a.a2=2;
push(&a_list, &a, sizeof(struct A));
find_a1_in_a_list(1,a_list);
struct Node *b_list = NULL;
struct B b;
b.b1=1;
b.b2=2;
push(&b_list, &b, sizeof(struct B));
find_b1_in_b_list(1,b_list);
//tried to make it generic
int search = 3;
find_generic(&search,offsetof(struct A, a2),a_list);
}
As you can I tried to makes generic search in function find_generic by passing the offset to the value in struct, that code works but only for int
but how can I pass to this generic function if I want to search int or long ,so I will know how to makes cast ?
Is there any way to cast void * by size so I can pass sizeof(int) or sizeof(long) and makes the casting by this value? or maybe another way?
Passing the compare function directly instead of playing with offsetof/sizeof will be more flexible:
struct Node *find_generic (struct Node *list,
int (*fn_cmp)(void const *a, void const *b),
void const *data)
{
while (list) {
if (fn_cmp(list->data, data) == 0)
break;
list = list->next;
}
return list;
}
and then create custom compare functions
static int cmp_A(void const *a_, void const *b_)
{
struct A const *a = a_;
struct A const *b = b_;
if (a->a1 == b->a1 && a->a2 == b->a2)
return 0;
return 1;
}
and call it like
struct A key = {
.a1 = 23,
.a2 = 42,
};
find_generic(a_list, cmp_A, &key);

All Nodes in a linked list point to same object

The problem is somewhere in here....
char buffer[80];
char *name;
while (1) {
fgets(buffer, 80, inf); //reads in at most 80 char from a line
if (feof(inf)) //this checks to see if the special EOF was read
break; //if so, break out of while and continue with your main
name = (char *) malloc(sizeof(char)*20);
....
name = strtok(buffer, " ");//get first token up to space
stock = newStock(name,...)
....
}
I'm working in C with generic linked lists. I made a list implementation that I've tested and know works with chars. I'm trying to add stocks (I created a stock struct) to the linked list, with each node of the linked list holding a stock struct, but when I finish reading in the stocks all of the nodes point to the same struct and I can't figure out why. Here's some snippets of my code
list *list = malloc(sizeof(list));
newList(list, sizeof(stock_t));
while(1) {
...
(read from file)
...
stock_t *stock;
stock = newStock(name, closes, opens, numshares, getPriceF, getTotalDollarAmountF,getPercentChangeF,toStringF);
addToBack(list, stock);
}
Here's the newStock function:
stock_t *newStock(char *name, float closingSharePrice, float openingSharePrice, int numberOfShares, getPrice getP, getTotalDollarAmount getTotal, getPercentChange getPercent, toString toStr) {
stock_t *stock = malloc(sizeof(stock));
stock->stockSymbol = name;
stock->closingSharePrice = closingSharePrice;
stock->openingSharePrice = openingSharePrice;
stock->numberOfShares = numberOfShares;
stock->getP = getP;
stock->getTotal = getTotal;
stock->getPercent = getPercent;
stock->toStr = toStr;
return stock;
}
In a way I see what's wrong. newStock returns a new pointer every time, but it always gets stored in the variable 'stock' which is what every node points to, so it's going to be equal to whatever the last pointer newStock returned was...but I don't see the way around this. I tried having newStock return just a stock_t, and doing addToBack(list, &stock), but that didn't solve the problem either.
Any help would be appreciated!
Here is some code from the list:
typedef struct node {
void *data;
struct node *next;
}node_t;
typedef struct {
int length;
int elementSize;
node_t *head;
node_t *tail;
} list;
void newList(list *list, int elementSize) {
assert(elementSize > 0);
list->length = 0;
list->elementSize = elementSize;
list->head = list->tail = NULL;
}
void addToBack(list *list, void *element) {
node_t *node = malloc(sizeof(node_t));
node->data = malloc(list->elementSize);
node->next = NULL; //back node
memcpy(node->data, element, list->elementSize);
if (list->length == 0) { //if first node added
list->head = list->tail = node;
}
else {
list->tail->next = node;
list->tail = node;
}
list->length++;
}
Here's code from the stock struct:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef float (*getPrice)(void *S);
typedef float (*getTotalDollarAmount)(void *S);
typedef float (*getPercentChange)(void *S);
typedef char *(*toString)(void *S);
typedef struct stock{
char *stockSymbol;
float closingSharePrice;
float openingSharePrice;
int numberOfShares;
getPrice getP;
getTotalDollarAmount getTotal;
getPercentChange getPercent;
toString toStr;
}stock_t;
The generic functions probably seem like overkill but this is for homework (if you couldn't tell already) so we were asked to specifically use them. I don't think that has anything to do with the problem though.
Here are the definitions for those functions anyway
float getPriceF(void *S) {
stock_t *stock = (stock_t*)S;
return stock->closingSharePrice;
}
float getTotalDollarAmountF(void *S) {
stock_t *stock = (stock_t*)S;
return ((stock->closingSharePrice) * (stock->numberOfShares));
}
float getPercentChangeF(void *S) {
stock_t *stock = (stock_t*)S;
return ((stock->closingSharePrice - stock->openingSharePrice)/(stock->openingSharePrice));
}
char *toStringF(void *S) {
stock_t* stock = (stock_t*)S;
char *name = malloc(20*sizeof(char));
//sprintf(name, "Symbol is: %s. ", (stock->stockSymbol));
return stock->stockSymbol;
}
void printStock(void *S) {
char *str = toStringF(S);
printf("%s \n", str);
}
And this is how I'm traversing the list:
typedef void (*iterate)(void *); //this is in the list.h file, just putting it here to avoid confusion
void traverse(list *list, iterate iterator) {
assert(iterator != NULL);
node_t *current = list->head;
while (current != NULL) {
iterator(current->data);
current = current->next;
}
}
And then in my main I just called
traverse(list, printStock);
I can't find any problems with your code (that would cause your problem, anyway - there are places where you don't check the return from malloc() and stuff like that, but those are not relevant to this question). You don't supply the definition of stock_t, so I made a new data struct, and a new couple of functions, otherwise I just copied and pasted the code you provided:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* Your code starts here */
typedef struct node {
void *data;
struct node *next;
}node_t;
typedef struct {
int length;
int elementSize;
node_t *head;
node_t *tail;
} list;
void newList(list *list, int elementSize) {
assert(elementSize > 0);
list->length = 0;
list->elementSize = elementSize;
list->head = list->tail = NULL;
}
void addToBack(list *list, void *element) {
node_t *node = malloc(sizeof(node_t));
node->data = malloc(list->elementSize);
node->next = NULL; //back node
memcpy(node->data, element, list->elementSize);
if (list->length == 0) { //if first node added
list->head = list->tail = node;
}
else {
list->tail->next = node;
list->tail = node;
}
list->length++;
}
/* Your code ends here */
/* I made a new struct, rather than stock, since you didn't supply it */
struct mydata {
int num1;
int num2;
};
/* I use this instead of newStock(), but it works the same way */
struct mydata * newNode(const int a, const int b) {
struct mydata * newdata = malloc(sizeof *newdata);
if ( newdata == NULL ) {
fputs("Error allocating memory", stderr);
exit(EXIT_FAILURE);
}
newdata->num1 = a;
newdata->num2 = b;
return newdata;
}
/* I added this function to check the list is good */
void printList(list * list) {
struct node * node = list->head;
int n = 1;
while ( node ) {
struct mydata * data = node->data;
printf("%d: %d %d\n", n++, data->num1, data->num2);
node = node->next;
}
}
/* Main function */
int main(void) {
list *list = malloc(sizeof(list));
newList(list, sizeof(struct mydata));
struct mydata * data;
data = newNode(1, 2);
addToBack(list, data);
data = newNode(3, 4);
addToBack(list, data);
data = newNode(5, 6);
addToBack(list, data);
printList(list);
return 0;
}
which outputs this:
paul#MacBook:~/Documents/src$ ./list
1: 1 2
2: 3 4
3: 5 6
paul#MacBook:~/Documents/src$
demonstrating that you have a 3 node list, with all nodes different and where you'd expect them to be.
Either there is some other problem in code you're not showing, or for some reason you are thinking each node points to the same struct when it actually doesn't.
One possibility is that you have a char * data member in your stock struct. It's impossible to tell from the code you provided, but it's possible that you really are creating different nodes, but they all end up pointing to the same name, so they just look like they're the same. If you're assigning a pointer to name, you should make sure it's freshly allocated memory each time, and that you're not just, for instance, strcpy()ing into the same memory and assigning the same address to each stock struct.
EDIT: Looks like that was your problem. This:
name = (char *) malloc(sizeof(char)*20);
....
name = strtok(buffer, " ");
should be:
name = (char *) malloc(sizeof(char)*20);
....
strcpy(name, strtok(buffer, " "));
Right now, you malloc() new memory and store a reference to it in name, but then you lose that reference and your memory when you overwrite it with the address returned from strtok(). Instead, you need to copy that token into your newly allocated memory, as shown.

pointers and values

I have written the following code, and it prints the root value correctly, but not the ret value. Here a memory address is potentially printed (1707388). I believe that ret could now be modified and the result would be seen in main. Any help is appreciated.
#include <stdlib.h>
struct node{
int value;
int order;
struct node *left;
struct node *right;
};
typedef struct node node_t;
node_t array[10];
void createTree(node_t *p, int order){
p->value = rand()%10;
p->order = order;
printf("%i", p->value);
printf(" ");
printf("%i\n", p->order);
if (!order){
p->left = NULL;
p->right = NULL;
return;
}
order--;
createTree(&p->left, order);
createTree(&p->right, order);
}
void traverse(node_t *current, node_t *ret, int size){
printf("%i\n", current->value);
if (current->value > size){
ret = current;
traverse(&current->left, &ret, size);
traverse(&current->right, &ret, size);
}
return;
}
int main(void){
node_t *root = &array[0];
node_t *ret;
srand(time(NULL));
createTree(root, 4);
int i = 3;
printf("%s", "root-value: ");
printf("%i\n", root->value);
traverse(root, ret, i);
printf("%s", "root-value: ");
printf("%i\n", root->value);
printf("%i\n", ret->value);
return 1;
}
This:
void createTree(node_t *p, int order)
Should be
void createTree(node_t **p, int order)
Otherwise you are modifying a local node_t pointer, instead of the one outside the function. Your tree isn't being built properly either.
You are passing ret by value to
void traverse(node_t *current, node_t *ret, int size){
When the function changes ret, the changes do not propagate back to the caller.
This means that ret in main() remains uninitialized, and the behaviour of your code is undefined.
To fix this, make traverse either return ret, or take it as node_t**.
There are few issues with the code.
First, you don't correctly allocate the memory for nodes. In your code, you are passing wrong pointer type, futhermore, pointer to uninitialized area.
Here, how it can be used differently:
node_t *createTree(int order)
{
node_t *result = malloc(sizeof(*result));
result->value = rand() % 10;
result->order = order;
if (order)
{
result->left = createTree(order - 1);
result->right = createTree(order - 1);
}
else
{
result->left = result->right = 0;
}
return result;
}
Then, your traverse function need some block to restrict agains failed search:
node_t *traverse(node_t *current, int size)
{
node_t *ret = NULL;
if (current->value > size)
{
// assuming current node fit - stops the search
ret = current;
}
if (!ret && current->left)
{
// try left node
ret = traverse(current->left, size);
}
if (!ret && current->right)
{
// try right node
ret = traverse(current->right, size);
}
return ret;
}
In case you need (usually you do), here is a destroyTree:
void destroyTree(node_t *node)
{
if (!node) return; // we treat NULL as a valid pointer for simplicity
destroyTree(node->left);
destroyTree(node->right);
free(node);
}
And here is a usage example:
node_t *root, *found;
root = createTree(4);
found = traverse(root, 3);
if (found)
{
printf("Found!");
}
destroyTree(root);
In traverse(node_t *current, node_t *ret, int size), ret is a stack variable. In other words, you are passing the pointer by value, instead of passing it by reference.
What have you done at the moment is essentially the same as:
int f(int i) {
...
i = <any value>;
...
}
In this case you are modifying only a copy of the value.
In your program, you are also modifying a copy of the pointer. Outside of the function the pointer stays not modified.
If you want to modify it, you need to pass a pointer to it:
void traverse(node_t *current, node_t **ret, int size){
...
*ret = current;
...
return;
}
The same for createTree().

Is it possible to have a linked list of different data types?

This is just another interview question.
Can we have a linked list of different data types, i.e. each element in a linked list can have different structure or union elements? If it's possible can you please explain with an example?
Well in a linked list you don't HAVE to link like for like structs together. As long as they have the appropriate forward and/or backwards pointers you are fine. For example:
struct BaseLink
{
BaseLink* pNext;
BaseLink* pPrev;
int typeId;
};
struct StringLink
{
BaseLink baseLink;
char* pString;
};
struct IntLink
{
BaseLink baseLink;
int nInt;
};
This way you'd have a linked list that goes from BaseLink to BaseLink. The extra data is not a problem. You want to see it as a StringLink? Then cast the BaseLink to a StringLink.
Just remember that you need some form of typeid in there so you know what to cast it to when you arrive at it.
Use union to create the datatype
union u_tag{
char ch;
int d;
double dl;
};
struct node {
char type;
union u_tag u;
struct node *next;
};
Use struct node to create linked list. type decides what is the datatype of the data.
Harsha T, Bangalore
You can use a union type:
enum type_tag {INT_TYPE, DOUBLE_TYPE, STRING_TYPE, R1_TYPE, R2_TYPE, ...};
struct node {
union {
int ival;
double dval;
char *sval;
struct recordType1 r1val;
struct recordType2 r2val;
...
} data;
enum type_tag dataType;
struct node *prev;
struct node *next;
};
Another method I've explored is to use a void* for the data and attach pointers to functions that handle the type-aware stuff:
/**
* Define a key type for indexing and searching
*/
typedef ... key_t;
/**
* Define the list node type
*/
struct node {
void *data;
struct node *prev;
struct node *next;
void *(*cpy)(void *); // make a deep copy of the data
void (*del)(void *); // delete the data
char *(*dpy)(void *); // format the data for display as a string
int (*match)(void *, key_t); // match against a key value
};
/**
* Define functions for handling a specific data type
*/
void *copyARecordType(void *data)
{
struct aRecordType v = *(struct aRecordType *) data;
struct aRecordType *new = malloc(sizeof *new);
if (new)
{
// copy elements of v to new
}
return new;
}
void deleteARecordType(void *data) {...}
char *displayARecordType(void *data) {...}
int matchARecordType(void *data, key_t key) {...}
/**
* Define functions for handling a different type
*/
void *copyADifferentRecordType(void *data) {...}
void deleteADifferentRecordType(void *data) {...}
char *displayADifferentRecordType(void *data) {...}
int matchADifferentRecordType(void *data, key_t key) {...}
/**
* Function for creating new list nodes
*/
struct node *createNode(void *data, void *(*cpy)(void *), void (*del)(void *),
char *(*dpy)(void *), int (*match)(void *, key_t))
{
struct node *new = malloc(sizeof *new);
if (new)
{
new->cpy = cpy;
new->del = del;
new->dpy = dpy;
new->match = match;
new->data = new->cpy(data);
new->prev = new->next = NULL;
}
return new;
}
/**
* Function for deleting list nodes
*/
void deleteNode(struct node *p)
{
if (p)
p->del(p->data);
free(p);
}
/**
* Add new node to the list; for this example, we just add to the end
* as in a FIFO queue.
*/
void addNode(struct node *head, void *data, void *(*cpy)(void*),
void (*del)(void *), char *(*dpy)(void *), int (*match)(void*, key_t))
{
struct node *new = createNode(data, cpy, del, dpy, match);
if (!head->next)
head->next = new;
else
{
struct node *cur = head->next;
while (cur->next != NULL)
cur = cur->next;
cur->next = new;
new->prev = cur;
}
}
/**
* Examples of how all of this would be used.
*/
int main(void)
{
struct aRecordType r1 = {...};
struct aDifferentRecordType r2 = {...};
struct node list, *p;
addNode(&list, &r1, copyARecordType, deleteARecordType, displayARecordType,
matchARecordType);
addNode(&list, &r2, copyADifferentRecordType, deleteADifferentRecordType,
displayADifferentRecordType, matchADifferentRecordType);
p = list.next;
while (p)
{
printf("Data at node %p: %s\n", (void*) p, p->dpy(p->data));
p = p->next;
}
return 0;
}
Obviously, I've left out some error checking and handling code from this example, and I don't doubt there are a host of problems with it, but it should be illustrative.
You can have each node in a linked list have a void* that points to your data. It's up to you how you determine what type of data that pointer is pointing to.
If you don't want to have to specify the type of every node in the list via the union solution you can always just store the data in a char* and take type-specific function pointers as parameters to type-sensitive operations such as printing or sorting the list.
This way you don't have to worry about what node is what type and can just cast the data however you like.
/* data types */
typedef struct list_node list_node;
struct list_node {
char *data;
list_node *next;
list_node *prev;
};
typedef struct list list;
struct list {
list_node *head;
list_node *tail;
size_t size;
};
/* type sensitive functions */
int list_sort(list *l, int (*compar)(const void*, const void*));
int list_print(list *l, void (*print)(char *data));
Yes, I do this by defining the list's element's value as a void pointer void*.
In order to know the type stored in each element of the list I also have a .type field in there, so I know how to dereference what the pointer is pointing to for each element.
struct node {
struct node* next;
int type;
void* value;
};
Here's a full example of this:
//
// An exercise to play with a struct that stores anything using a void* field.
//
#include <stdio.h>
#define TRUE 1
int TYPE_INT = 0;
int TYPE_STRING = 1;
int TYPE_BOOLEAN = 2;
int TYPE_PERSON = 3;
struct node {
struct node* next;
int type;
void* value;
};
struct person {
char* name;
int age;
};
int main(int args, char **argv) {
struct person aPerson;
aPerson.name = "Angel";
aPerson.age = 35;
// Define a linked list of objects.
// We use that .type field to know what we're dealing
// with on every iteration. On .value we store our values.
struct node nodes[] = {
{ .next = &nodes[1], .type = TYPE_INT , .value=1 },
{ .next = &nodes[2], .type = TYPE_STRING , .value="anyfing, anyfing!" },
{ .next = &nodes[3], .type = TYPE_PERSON , .value=&aPerson },
{ .next = NULL , .type = TYPE_BOOLEAN, .value=TRUE }
};
// We iterate through the list
for ( struct node *currentNode = &nodes[0]; currentNode; currentNode = currentNode->next) {
int currentType = (*currentNode).type;
if (currentType == TYPE_INT) {
printf("%s: %d\n", "- INTEGER", (*currentNode).value); // just playing with syntax, same as currentNode->value
} else if (currentType == TYPE_STRING) {
printf("%s: %s\n", "- STRING", currentNode->value);
} else if (currentType == TYPE_BOOLEAN) {
printf("%s: %d\n", "- BOOLEAN (true:1, false:0)", currentNode->value);
} else if (currentType == TYPE_PERSON) {
// since we're using void*, we end up with a pointer to struct person, which we *dereference
// into a struct in the stack.
struct person currentPerson = *(struct person*) currentNode->value;
printf("%s: %s (%d)\n","- TYPE_PERSON", currentPerson.name, currentPerson.age);
}
}
return 0;
}
Expected output:
- INTEGER: 1
- STRING: anyfing, anyfing!
- TYPE_PERSON: Angel (35)
- BOOLEAN (true:1, false:0): 1
As said, you can have a node this questionwith a void*. I suggest using something to know about your type :
typedef struct
{
/* linked list stuff here */
char m_type;
void* m_data;
}
Node;
See this question.
Actually, you don't have to put the pointer first in the structure, you can put it anywhere and then find the beginning fo the struct with a containerof() macro. The linux kernel does this with its linked lists.
http://isis.poly.edu/kulesh/stuff/src/klist/
I use these macros I wrote to make general linked lists. You just create your own struct and use the macro list_link somewhere as a member of the struct. Give that macro one argument naming the struct (without the struct keyword). This implements a doubly linked list without a dummy node (e.g. last node links back around to first node). The anchor is a pointer to the first node which starts out initialized by list_init(anchor) by giving it the lvalue (a dereferenced pointer to it is an lvalue). Then you can use the other macros in the header. Read the source for comments about each available macro functions. This is implemented 100% in macros.
http://phil.ipal.org/pre-release/list-0.0.5.tar.bz2
Yes,Sure You can insert any data type values in the linked list I've designed and its very simple to do so.I have used different constructors of node and boolean variables to check that which type value is inserted and then I do operation and command according to that value in my program.
//IMPLEMENTATION OF SINGLY LINKED LISTS
#include"iostream"
#include"conio.h"
#include <typeinfo>
using namespace std;
class node //struct
{
public:
node* nextptr;
int data;
////////////////////////////////just to asure that user can insert any data type value in the linked list
string ss;
char cc;
double dd;
bool stringTrue=0;
bool intTrue = 0;
bool charTrue = 0;
bool doubleTrue = 0;
////////////////////////////////just to asure that user can insert any data type value in the linked list
node()
{
nextptr = NULL;
}
node(int d)
{
data = d;
nextptr = NULL;
intTrue = 1;
}
////////////////////////////////just to asure that user can insert any data type value in the linked list
node(string s)
{
stringTrue = 1;
ss = s;
nextptr = NULL;
}
node(char c)
{
charTrue = 1;
cc = c;
nextptr = NULL;
}
node(double d)
{
doubleTrue = 1;
dd = d;
nextptr = NULL;
}
////////////////////////////////just to asure that user can insert any data type value in the linked list
//TO Get the data
int getintData()
{
return data;
}
string getstringData()
{
return ss;
}
double getdoubleData()
{
return dd;
}
char getcharData()
{
return cc;
}
//TO Set the data
void setintData(int d)
{
data = d;
}
void setstringData(string s)
{
ss = s;
}
void setdoubleData(double d)
{
dd = d;
}
void setcharData(char c)
{
cc = c;
}
char checkWhichInput()
{
if (intTrue == 1)
{
return 'i';
}
else if (stringTrue == 1)
{
return 's';
}
else if (doubleTrue == 1)
{
return 'd';
}
else if (charTrue == 1)
{
return 'c';
}
}
//////////////////////////////Just for the sake of implementing for any data type//////////////////////////////
node* getNextptr()
{
return nextptr;
}
void setnextptr(node* nptr)
{
nextptr = nptr;
}
};
class linkedlist
{
node* headptr;
node* addnodeatspecificpoition;
public:
linkedlist()
{
headptr = NULL;
}
void insertionAtTail(node* n)
{
if (headptr == NULL)
{
headptr = n;
}
else
{
node* rptr = headptr;
while (rptr->getNextptr() != NULL)
{
rptr = rptr->getNextptr();
}
rptr->setnextptr(n);
}
}
void insertionAtHead(node *n)
{
node* tmp = n;
tmp->setnextptr(headptr);
headptr = tmp;
}
int sizeOfLinkedList()
{
int i = 1;
node* ptr = headptr;
while (ptr->getNextptr() != NULL)
{
++i;
ptr = ptr->getNextptr();
}
return i;
}
bool isListEmpty() {
if (sizeOfLinkedList() <= 1)
{
return true;
}
else
{
false;
}
}
void insertionAtAnyPoint(node* n, int position)
{
if (position > sizeOfLinkedList() || position < 1) {
cout << "\n\nInvalid insertion at index :" << position;
cout <<".There is no index " << position << " in the linked list.ERROR.\n\n";
return;
}
addnodeatspecificpoition = new node;
addnodeatspecificpoition = n;
addnodeatspecificpoition->setnextptr(NULL);
if (headptr == NULL)
{
headptr = addnodeatspecificpoition;
}
else if (position == 0)
{
addnodeatspecificpoition->setnextptr(headptr);
headptr = addnodeatspecificpoition;
}
else
{
node* current = headptr;
int i = 1;
for (i = 1; current != NULL; i++)
{
if (i == position)
{
addnodeatspecificpoition->setnextptr(current->getNextptr());
current->setnextptr(addnodeatspecificpoition);
break;
}
current = current->getNextptr();
}
}
}
friend ostream& operator<<(ostream& output,const linkedlist& L)
{
char checkWhatInput;
int i = 1;
node* ptr = L.headptr;
while (ptr->getNextptr() != NULL)
{
++i;
checkWhatInput = ptr->checkWhichInput();
/// <summary>
switch (checkWhatInput)
{
case 'i':output <<ptr->getintData()<<endl;
break;
case 's':output << ptr->getstringData()<<endl;
break;
case 'd':output << ptr->getdoubleData() << endl;
break;
case 'c':output << ptr->getcharData() << endl;
break;
default:
break;
}
/// </summary>
/// <param name="output"></param>
/// <param name="L"></param>
/// <returns></returns>
ptr = ptr->getNextptr();
}
/// <summary>
switch (checkWhatInput)
{
case 'i':output << ptr->getintData() << endl;
break;
case 's':output << ptr->getstringData() << endl;
break;
case 'd':output << ptr->getdoubleData() << endl;
break;
case 'c':output << ptr->getcharData() << endl;
break;
default:
break;
}
/// </summary>
/// <param name="output"></param>
/// <param name="L"></param>
/// <returns></returns>
if (ptr->getNextptr() == NULL)
{
output << "\nNULL (There is no pointer left)\n";
}
return output;
}
~linkedlist() {
delete addnodeatspecificpoition;
}
};
int main()
{
linkedlist L1;
//Insertion at tail
L1.insertionAtTail(new node("dsaf"));
L1.insertionAtTail(new node("sadf"));
L1.insertionAtTail(new node("sfa"));
L1.insertionAtTail(new node(12));
L1.insertionAtTail(new node(67));
L1.insertionAtTail(new node(23));
L1.insertionAtTail(new node(45.677));
L1.insertionAtTail(new node(12.43556));
//Inserting a node at head
L1.insertionAtHead(new node(1));
//Inserting a node at any given point
L1.insertionAtAnyPoint(new node(999), 3);
cout << L1;
cout << "\nThe size of linked list after insertion of elements is : " << L1.sizeOfLinkedList();
}
The output is
1
dsaf
sadf
999
sfa
12
67
23
45.677
12.4356
Thats what you can use to create a linked list without worrying of data type
Just an FYI, In C# you can use Object as your data member.
class Node
{
Node next;
Object Data;
}
User can then use something like this to find out which Object the Node stores:
if (obj.GetType() == this.GetType()) //
{
}

Resources