linked lists - virginia techc linked list computer organization i 1 cs@vt ©2005-2019 mcquain linked...

27
C Linked List Computer Organization I 1 CS@VT ©2005-2019 McQuain Linked Lists A linked list is a data structure that uses a "chain" of node objects, connected by pointers, to organize a collection of user data values. Here's a fairly typical conceptual view of a doubly-linked list: Head node Tail node 41 32 17 19 12 33 5 23 8 27

Upload: others

Post on 27-Jan-2021

4 views

Category:

Documents


0 download

TRANSCRIPT

  • C Linked List

    Computer Organization I

    1

    CS@VT ©2005-2019 McQuain

    Linked Lists

    A linked list is a data structure that uses a "chain" of node objects, connected by pointers,

    to organize a collection of user data values.

    Here's a fairly typical conceptual view of a doubly-linked list:

    Head node

    Tail node

    41

    32

    17

    19

    12

    33

    5

    23

    8

    27

  • C Linked List

    Computer Organization I

    2

    CS@VT ©2005-2019 McQuain

    Structural Considerations

    Front Guard

    Rear Guard

    19

    12

    The use of "guard" nodes at the front and rear of a list eliminate any "special cases" when

    implementing insertion/deletion operations.

    This way, every "data" node will lie between two nodes.

    The common alternative is to simply have pointers to the first and last data nodes, probably

    stored in a list object. That leads to special cases when operating at the front or rear of the

    list.

  • C Linked List

    Computer Organization I

    3

    CS@VT ©2005-2019 McQuain

    Minimal Linked List Interface

    A linked list implementation will typically provide at least:

    - initialization function to set up basic structure for an empty list

    - insert functions to add new element to the list; at front, at rear, at user-selected

    position, ordered insertion

    - remove function to remove element from the list

    - find function to determine whether a given element occurs in the list

    - clear function to restore the list to an empty state

    In C we would organize this as a pair of struct types (list and node) and a collection of

    associated functions.

  • C Linked List

    Computer Organization I

    4

    CS@VT ©2005-2019 McQuain

    Generic Node and List

    #ifndef DLIST_H

    #define DLIST_H

    // List node:

    struct _DNode {

    struct _DNode *prev; // points toward front guard

    struct _DNode *next; // points toward rear guard

    };

    typedef struct _DNode DNode;

    // List object:

    struct _DList {

    DNode fGuard; // front guard node for list

    DNode rGuard; // rear guard node for list

    };

    typedef struct _DList DList;

    #endif

  • C Linked List

    Computer Organization I

    5

    CS@VT ©2005-2019 McQuain

    DList Initialization

    An empty DList will be constructed as shown below:

    DList object

    DNode fGuard DNode rGuard

    DNode* prev

    DNode* next DNode* next

    DNode* prev

    This eliminates special cases, because every data node will always be between two other

    nodes.

    We could also make fGuard.prev point to rGuard and rGuard.next point to

    fGuard, which would eliminate NULL pointers and allow the list to be used in a circular

    fashion.

  • C Linked List

    Computer Organization I

    6

    CS@VT ©2005-2019 McQuain

    Using the DList as a Generic

    The DList implementation shown here:

    - makes no provision for a client to "attach" data to the nodes of a DList

    - makes no provision for a DList to "know" anything about the data a client might

    organize by creating a DList

    This approach is based upon the idea that there will be an "adaptor" to mediate between a client's code and the DList interface so that:

    - the client encounters no details of the DList internals

    - the DList "knows" nothing about the client's data, not even that the client's data

    exists

    - the adaptor supplies a layer of interpretation between the client and the DList

  • C Linked List

    Computer Organization I

    7

    CS@VT ©2005-2019 McQuain

    Using the DList as a Generic

    The basic logic of the approach is shown below:

    &node†

    &node†

    &node†

    Client's world

    data objects

    compare()

    Adaptor's world

    aggregators

    data object

    node

    insert()

    remove()

    find()

    compare()

    DList's world

    nodes

    insert()

    remove()

    find()

    &node†

    † inside an

    aggregator

    ‡ as a void*

  • C Linked List

    Computer Organization I

    8

    CS@VT ©2005-2019 McQuain

    Anatomy of a Find Operation

    Client

    &X

    node1

    data object

    X

    find( void* pData )

    AdaptorDList

    find( DNode* pNode )

    compare( DNode* pNode, DNode* pCurr )

    compare( void* p1, void* p2 )

    cre

    ate

    s

    cre

    ate

    s

    &X

    &node

    traverses list

    to a DNode

    in the list

    pData2

    node2

    &node1

    &node2

    &XpData2

    ?

    &nodepData2

  • C Linked List

    Computer Organization I

    9

    CS@VT ©2005-2019 McQuain

    What's an "Aggregator"?

    The adaptor uses the following type to bundle user data with a DNode:

    struct _DListAggregator {

    void* userData;

    DNode node;

    };

    typedef struct _DListAggregator DListAggregator;

    Neither the client nor the DList ever access these aggregators directly.

    They are created by the adaptor when it manages the insertion of a client data object to the DList.

    They are destroyed when no longer needed (exactly when and how is left as a puzzle).

  • C Linked List

    Computer Organization I

    10

    CS@VT ©2005-2019 McQuain

    Structure of a Generic List

    adaptor a device that converts attributes of one device or system to those of an

    otherwise incompatible device or system

    DList

    fGuard

    rGuard

    aggregator

    created by the

    adaptor

    payload

    created by the

    client

  • C Linked List

    Computer Organization I

    11

    CS@VT ©2005-2019 McQuain

    Adaptor Interface

    The adaptor must provide the client with "mediators" for the DList interface:

    /** Prefix user's data object (encapsulated in a DListAggregator)

    * to a DList.

    */

    void DListAdaptor_PushFront(DList* const pList, void* const pData);

    /** Append user's data object (encapsulated in a DListAggregator)

    * to a DList.

    */

    void DListAdaptor_PushBack(DList* const pList, void* const pData);

    /** Insert user's data object (encapsulated in a DListAggregator)

    * to a DList, placing it in the order determined by the user's

    * compare() function.

    */

    void DListAdaptor_PushOrdered(DList* const pList,

    void* const pData);

    . . .

    // Additional functions are shown at the end of these notes ...

  • C Linked List

    Computer Organization I

    12

    CS@VT ©2005-2019 McQuain

    Using a Client-supplied Function

    The adaptor function shown below raises an issue:

    /** Insert user's data object (encapsulated in a DListAggregator)

    * to a DList, placing it in the order determined by the user's

    * compare() function.

    */

    void DListAdaptor_PushOrdered(DList* const pList,

    In C, a function name is actually a pointer, which means:

    - we can pass access to a function as a parameter to another function

    - we can store access to a function as a pointer

  • C Linked List

    Computer Organization I

    13

    CS@VT ©2005-2019 McQuain

    Using a Client-supplied Function

    The adaptor can receive the client's comparison function via a function call (from the

    client):

    // In DListAdaptor.c:

    static int32_t (*Client_compare)(const void* const pLeft,

    const void* const pRight);

    /** Install client-side comparison function.

    */

    void DListAdaptor_Setup(

    int32_t (*compare)(const void* const pLeft, const void* const pRight)) {

    Client_compare = compare;

    }

    Like any other file-scoped variable, Client_compare has static duration.

    Other DListAdaptor functions could then call the client's function:

    . . .

    return Client_compare((void*) leftAgg->userData,

    (void*) rightAgg->userData);

    . . .

  • C Linked List

    Computer Organization I

    14

    CS@VT ©2005-2019 McQuain

    The DList Uses the Adaptor

    The DList implementation is modified to be aware of the adaptor's comparison function:

    struct _DList {

    DNode fGuard; // front sentinel node

    DNode rGuard; // rear sentinel node

    // The following function is supplied by the DListAdaptor type, and

    // depends on the matching function supplied by the client;

    // pointer to the DListAdaptor comparison function

    int32_t (*Adaptor_compare)(const DNode* const left,

    const DNode* const right);

    };

    The DList receives this function pointer via the DList initializer.

  • C Linked List

    Computer Organization I

    15

    CS@VT ©2005-2019 McQuain

    DList Uses Adaptor Uses Client

    The DList implementation calls the adaptor's comparison function:

    DNode* DList_Find(const DList* const pList, const DNode* const pNode) {

    DNode* current = pList->fGuard.next;

    while ( current != &pList->rGuard ) {

    if ( pList->Adaptor_compare(current, pNode) == 0 ) {

    return current;

    }

    current = current->next;

    }

    return NULL;

    }

    And the adaptor's comparison function calls the client's comparison function:

    int32_t DListAdaptor_compare(const DNode* const leftNode,

    const DNode* const rightNode) {

    const DListAggregator* leftWrapper = nodeToWrapper(leftNode);

    const DListAggregator* rightWrapper = nodeToWrapper(rightNode);

    return Client_compare((void*) leftWrapper->userData,

    (void*) rightWrapper->userData);

    }

  • C Linked List

    Computer Organization I

    16

    CS@VT ©2005-2019 McQuain

    Imposition on the Client

    The design of the adaptor's comparison function imposes a constraint on the client:

    int32_t DListAdaptor_compare(const DNode* const leftNode,

    const DNode* const rightNode) {

    . . .

    return Client_compare((void*) leftWrapper->userData,

    (void*) rightWrapper->userData);

    }

    The client must implement a comparison function with the appropriate interface:

    int32_t compare(void* pLeft, void* pRight);

  • C Linked List

    Computer Organization I

    17

    CS@VT ©2005-2019 McQuain

    Node Pointer to Aggregator Pointer

    The adaptor's compare function receives DNode pointers as parameters:

    . . .

    const DListAggregator* leftWrapper = nodeToWrapper(leftNode);

    const DListAggregator* rightWrapper = nodeToWrapper(rightNode);

    . . .

    int32_t DListAdaptor_compare(const DNode* const leftNode,

    const DNode* const rightNode) {

    . . .

    But, it needs to call the client's compare function, which takes (void) pointers to client data

    objects:

    int32_t SiteAddress_compare(const void* const pLeft,

    const void* const pRight);

    . . .

    This is accomplished by using a static function in the adaptor implementation:

  • C Linked List

    Computer Organization I

    18

    CS@VT ©2005-2019 McQuain

    Accessing the Aggregator

    We want a pointer pDA that points to the aggregator object.

    How do we get it?

    payload

    DListAggregator

    prev

    next

    pNode

    Suppose we have a pointer pNode to a DNode inside an aggregator:

    pDA

  • C Linked List

    Computer Organization I

    19

    CS@VT ©2005-2019 McQuain

    Accessing the Aggregator

    Then it appears we can set the value for pDA by doing something like this:

    pDA = (uint8_t*)pNode – sizeof(userData)

    . . .

    prev

    next

    userData

    . . .

    memory

    incre

    asin

    g a

    dd

    ress

    es

    ag

    gre

    gato

    r

    DN

    od

    e

    pNode

    pDA

    Suppose the aggregator is laid out in memory as shown below:

    … but that logic depends on the specific memory layout shown above ...

    … and that is not guaranteed. The Standard leaves this to the compiler writer.

  • C Linked List

    Computer Organization I

    20

    CS@VT ©2005-2019 McQuain

    offsetof() to the Rescue!

    offsetof(type, member-designator)

    expands to an integer constant expression that has type size_t, the value of which

    is the offset in bytes, to the structure member (designated by member-designator),

    from the beginning of its structure (designated by type).

    The Standard Library includes a relevant C macro:

    member1

    member2

    member3

    member4

    . . .

    offset of

    member3 struct F {

    member1;

    member2;

    member3;

    member4;

    };

    offsetof(F, member3)

  • C Linked List

    Computer Organization I

    21

    CS@VT ©2005-2019 McQuain

    So…

    Given the situation described before:

    Then, the address of the aggregator object would (almost) be given by:

    pNode – offsetof(DListAggregator, node)

    We just need to throw in a couple of typecasts to fix the pointer arithmetic:

    (DListAggregator*)( (uint8_t*)(pNode) –

    offsetof(DListAggregator, node) )

    payload

    DListAggregator

    prev

    next

    pNode

    pDA

  • C Linked List

    Computer Organization I

    22

    CS@VT ©2005-2019 McQuain

    Node Pointer to Aggregator Pointer

    The adaptor uses the following function to compute DNode pointers as parameters:

    static DListAggregator* nodeToWrapper(const DNode* const pNode) {

    return ((DListAggregator*)((uint8_t*)pNode –

    offsetof(DListAggregator, node)));

    }

  • C Linked List

    Computer Organization I

    23

    CS@VT ©2005-2019 McQuain

    Traversing the DList

    void traverseList(DList* pL) {

    DNode* e = DList_Head(pL);

    while ( (e = DList_Next(e)) != DList_End(pL)) {

    // Get pointer to the "duct-tape" object from

    // the pointer to the DList element:

    IntegerDT *p = DList_Entry(e, IntegerDT, node);

    // Get value of payload within "duct-tape" object:

    int userData = p->payload;

    // do stuff with current user data element

    }

    }

  • C Linked List

    Computer Organization I

    24

    CS@VT ©2005-2019 McQuain

    Full DList Interface

    // Create a new, empty DList.

    DList* DList_Create(int32_t (*compare)(const DNode* const left,

    const DNode* const right));

    // Return whether DList is empty.

    bool DList_Empty(const DList* const pList);

    //////////////////////////////////////////////////// insertion fns

    // Insert *pNode as first interior element of *pList.

    void DList_PushFront(DList* const pList, DNode* const pNode);

    // Insert *pNode as last interior element of *pList.

    void DList_PushBack(DList* const pList, DNode* const pNode);

    // Insert *pNode in ascending order according to the adaptor's

    // comparison function.

    void DList_PushOrdered(DList* const pList, DNode* const pNode);

    . . .

    Here's the full DList interface:

  • C Linked List

    Computer Organization I

    25

    CS@VT ©2005-2019 McQuain

    Full DList Interface

    . . .

    //////////////////////////////////////////////////////// search fn

    // Search list for an element equal to user-supplied data object.

    DNode* DList_Find(const DList* const pList, const DNode* const pNode);

    ////////////////////////////////////////////////////// deletion fns

    // Remove first interior element of *pList and return it.

    DNode* DList_PopFront(DList* const pList);

    // Remove last interior element of *pList and return it.

    DNode* DList_PopBack(DList* const pList);

    // Remove element equal to user-supplied data object.

    DNode* DList_Remove(DList* pList, const DNode* const pNode);

  • C Linked List

    Computer Organization I

    26

    CS@VT ©2005-2019 McQuain

    Full Adaptor Interface

    Here's the full adaptor interface:

    /////////////////////////////////////////// adaptors for DList insertion fns

    // Prefixes user's data object (encapsulated in an aggregator) to a DList.

    void DListAdaptor_PushFront(DList* const pList, void* const pData);

    // Appends user's data object (encapsulated in an aggregator) to a DList.

    void DListAdaptor_PushBack(DList* const pList, void* const pData);

    // Inserts user's data object (encapsulated in an aggregator) to a DList,

    // placing it in the order determined by the user's compare() function.

    void DListAdaptor_PushOrdered(DList* const pList, void* const pData);

    ////////////////////////////////////////////// adaptors for DList search fn

    // Searches a DList for an occurrence of a given user data object.

    void* DListAdaptor_Find(const DList* const pList, const void* const pData);

    . . .

  • C Linked List

    Computer Organization I

    27

    CS@VT ©2005-2019 McQuain

    Full Adaptor Interface

    Here's the full adaptor interface:

    . . .

    /////////////////////////////////////////////// adaptors for DList remove fns

    // Removes/returns the first element in the list.

    void* DListAdaptor_PopFront(DList* const pList);

    // Removes/returns the last element in the list.

    void* DListAdaptor_PopBack(DList* const pList);

    // Removes/returns the first element in the list that matches *pData,

    // according to the user's compare function.

    void* DListAdaptor_Remove(DList* const pList, const void* const pData);