Some different designs --- One struct type only
Important Note - these designs all depend on subverting the type system
in one way or another, and that should make you nervous.
The first method is not really so bad, if you always practice safe
functions. See C:ARM Sec. 5.6.4 for the layout rules of struct
components. Each node simply stores a pointer to data that is
supplied elsewhere. What makes this dangerous is that the node
has no information about where the pointer came from, or what kind of
thing it points to. You could add another field, to indicate the
type of data stored here, but that still relies on programmer restraint
for correctness.
struct node {
struct node *next;
struct node *prev;
void *data;
}
struct node *node_create(void *data)
{
struct node *ptr = malloc(sizeof(struct node));
// verify ptr is not NULL
ptr->next = NULL;
ptr->prev = NULL;
ptr->data = data;
return ptr;
}
This is an old-style hack, that relies on the lack of bounds-checking
in C.
struct node {
struct node *next;
struct node *prev;
char data[1];
}
struct node *node_create(int size, char *data)
{
struct node *ptr = malloc(sizeof(struct node) +
(size-1));
// verify ptr is not NULL
ptr->next = NULL;
ptr->prev = NULL;
for (int i = 0; i < size; i++)
ptr->data[i] = data[i];
return ptr;
}
This is a new-style hack, that uses the C99 flexible array member,
described in C:ARM Sec. 5.6.8. This is better because there are
actual semantic rules now, and we took the precaution of storing the
array length.
struct node {
struct node *next;
struct node *prev;
int data_len;
char data[];
}
struct node *node_create(int size, char *data)
{
struct node *ptr = malloc(sizeof(struct node) +
size);
// verify ptr is not NULL
ptr->next = NULL;
ptr->prev = NULL;
ptr->data_len = size;
for (int i = 0; i < size; i++)
ptr->data[i] = data[i];
return ptr;
}
Note that data_len
and size
probably should
be declared as size_t,
not as int
.