'Multipurpose' linked list implementation in pure C

前端 未结 9 1815
一生所求
一生所求 2020-12-12 16:33

This is not exactly a technical question, since I know C kind of enough to do the things I need to (I mean, in terms of not \'letting the language get in your way\'), so thi

9条回答
  •  星月不相逢
    2020-12-12 17:21

    A void * is a bit of a pain in a linked list since you have to manage it's allocation separately to the list itself. One approach I've used in the past is to have a 'variable sized' structure like:

    typedef struct _tNode {
        struct _tNode *prev;
        struct _tNode *next;
        int payloadType;
        char payload[1];  // or use different type for alignment.
    } tNode;
    

    Now I realize that doesn't look variable sized but let's allocate a structure thus:

    typedef struct {
        char Name[30];
        char Addr[50];
    } tPerson;
    tNode *node = malloc (sizeof (tNode) - 1 + sizeof (tPerson));
    

    Now you have a node that, for all intents and purposes, looks like this:

    typedef struct _tNode {
        struct _tNode *prev;
        struct _tNode *next;
        int payloadType;
        char Name[30];
        char Addr[50];
    } tNode;
    

    or, in graphical form (where [n] means n bytes):

    +----------------+
    |    prev[4]     |
    +----------------+
    |    next[4]     |
    +----------------+
    | payloadType[4] |                
    +----------------+                +----------+
    |   payload[1]   | <- overlap ->  | Name[30] |
    +----------------+                +----------+
                                      | Addr[50] |
                                      +----------+
    

    That is, assuming you know how to address the payload correctly. This can be done as follows:

    node->prev = NULL;
    node->next = NULL;
    node->payloadType = PLTYP_PERSON;
    tPerson *person = &(node->payload); // cast for easy changes to payload.
    strcpy (person->Name, "Bob Smith");
    strcpy (person->Addr, "7 Station St");
    

    That cast line simply casts the address of the payload character (in the tNode type) to be an address of the actual tPerson payload type.

    Using this method, you can carry any payload type you want in a node, even different payload types in each node, without the wasted space of a union. This wastage can be seen with the following:

    union {
        int x;
        char y[100];
    } u;
    

    where 96 bytes are wasted every time you store an integer type in the list (for a 4-byte integer).

    The payload type in the tNode allows you to easily detect what type of payload this node is carrying, so your code can decide how to process it. You can use something along the lines of:

    #define PAYLOAD_UNKNOWN     0
    #define PAYLOAD_MANAGER     1
    #define PAYLOAD_EMPLOYEE    2
    #define PAYLOAD_CONTRACTOR  3
    

    or (probably better):

    typedef enum {
        PAYLOAD_UNKNOWN,
        PAYLOAD_MANAGER,
        PAYLOAD_EMPLOYEE,
        PAYLOAD_CONTRACTOR
    } tPayLoad;
    

提交回复
热议问题