Linked Lists C Linked List 1 A linked

  • Slides: 27
Download presentation
Linked Lists C Linked List 1 A linked list is a data structure that

Linked Lists C Linked List 1 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 12 17 41 5 19 27 Tail node [email protected] 8 23 33 Computer Organization I 32 © 2005 -2019 Mc. Quain

Structural Considerations C Linked List 2 The use of "guard" nodes at the front

Structural Considerations C Linked List 2 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. Front Guard 12 Rear Guard 19 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. [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Minimal Linked List Interface C Linked List 3 A linked list implementation will typically

Minimal Linked List Interface C Linked List 3 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. [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Generic Node and List C Linked List 4 #ifndef DLIST_H #define DLIST_H // List

Generic Node and List C Linked List 4 #ifndef DLIST_H #define DLIST_H // List node: struct _DNode { struct _DNode *prev; struct _DNode *next; // points toward front guard // points toward rear guard }; typedef struct _DNode; // List object: struct _DList { DNode f. Guard; DNode r. Guard; // front guard node for list // rear guard node for list }; typedef struct _DList; #endif [email protected] Computer Organization I © 2005 -2019 Mc. Quain

DList Initialization C Linked List 5 An empty DList will be constructed as shown

DList Initialization C Linked List 5 An empty DList will be constructed as shown below: DList object DNode* prev DNode* next DNode f. Guard DNode r. Guard This eliminates special cases, because every data node will always be between two other nodes. We could also make f. Guard. prev point to r. Guard and r. Guard. next point to f. Guard, which would eliminate NULL pointers and allow the list to be used in a circular fashion. [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Using the DList as a Generic C Linked List 6 The DList implementation shown

Using the DList as a Generic C Linked List 6 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 [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Using the DList as a Generic C Linked List 7 The basic logic of

Using the DList as a Generic C Linked List 7 The basic logic of the approach is shown below: Client's world data objects &d &d ata compare() ata &d ata ob je ct ‡ ob jec ob t‡ jec DList's world aggregators data object nodes &node† insert() &node† remove() &node† find() a at &d t‡ Adaptor's world ob find() t‡ jec compare() ‡ [email protected] as a void* &node† † inside an aggregator Computer Organization I © 2005 -2019 Mc. Quain

Anatomy of a Find Operation C Linked List p. Data 2 &node Adaptor DList

Anatomy of a Find Operation C Linked List p. Data 2 &node Adaptor DList Client creates find( void* p. Data ) find( DNode* p. Node ) creates data object X &X 8 traverses list to a DNode in the list &X &node p. Data 2 node 2 &node 1 &node 2 &X p. Data 2 compare( DNode* p. Node, DNode* p. Curr ) compare( void* p 1, void* p 2 ) <, ==, > ? [email protected] Computer Organization I © 2005 -2019 Mc. Quain

What's an "Aggregator"? C Linked List 9 The adaptor uses the following type to

What's an "Aggregator"? C Linked List 9 The adaptor uses the following type to bundle user data with a DNode: struct _DList. Aggregator { void* user. Data; DNode node; }; typedef struct _DList. Aggregator; 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). [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Structure of a Generic List adaptor C Linked List 10 a device that converts

Structure of a Generic List adaptor C Linked List 10 a device that converts attributes of one device or system to those of an otherwise incompatible device or system aggregator created by the adaptor DList f. Guard r. Guard payload created by the client [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Adaptor Interface C Linked List 11 The adaptor must provide the client with "mediators"

Adaptor Interface C Linked List 11 The adaptor must provide the client with "mediators" for the DList interface: /** Prefix user's data object (encapsulated in a DList. Aggregator) * to a DList. */ void DList. Adaptor_Push. Front(DList* const p. List, void* const p. Data); /** Append user's data object (encapsulated in a DList. Aggregator) * to a DList. */ void DList. Adaptor_Push. Back(DList* const p. List, void* const p. Data); /** Insert user's data object (encapsulated in a DList. Aggregator) * to a DList, placing it in the order determined by the user's * compare() function. */ void DList. Adaptor_Push. Ordered(DList* const p. List, void* const p. Data); . . . // Additional functions are shown at the end of these notes. . . [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Using a Client-supplied Function C Linked List 12 The adaptor function shown below raises

Using a Client-supplied Function C Linked List 12 The adaptor function shown below raises an issue: /** Insert user's data object (encapsulated in a DList. Aggregator) * to a DList, placing it in the order determined by the user's * compare() function. */ void DList. Adaptor_Push. Ordered(DList* const p. List, 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 [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Using a Client-supplied Function C Linked List 13 The adaptor can receive the client's

Using a Client-supplied Function C Linked List 13 The adaptor can receive the client's comparison function via a function call (from the client): // In DList. Adaptor. c: static int 32_t (*Client_compare)(const void* const p. Left, const void* const p. Right); /** Install client-side comparison function. */ void DList. Adaptor_Setup( int 32_t (*compare)(const void* const p. Left, const void* const p. Right)) { Client_compare = compare; } Like any other file-scoped variable, Client_compare has static duration. Other DList. Adaptor functions could then call the client's function: . . . return Client_compare((void*) left. Agg->user. Data, (void*) right. Agg->user. Data); . . . [email protected] Computer Organization I © 2005 -2019 Mc. Quain

The DList Uses the Adaptor C Linked List 14 The DList implementation is modified

The DList Uses the Adaptor C Linked List 14 The DList implementation is modified to be aware of the adaptor's comparison function: struct _DList { DNode f. Guard; DNode r. Guard; // front sentinel node // rear sentinel node // The following function is supplied by the DList. Adaptor type, and // depends on the matching function supplied by the client; // pointer to the DList. Adaptor comparison function int 32_t (*Adaptor_compare)(const DNode* const left, const DNode* const right); }; The DList receives this function pointer via the DList initializer. [email protected] Computer Organization I © 2005 -2019 Mc. Quain

DList Uses Adaptor Uses Client C Linked List 15 The DList implementation calls the

DList Uses Adaptor Uses Client C Linked List 15 The DList implementation calls the adaptor's comparison function: DNode* DList_Find(const DList* const p. List, const DNode* const p. Node) { DNode* current = p. List->f. Guard. next; while ( current != &p. List->r. Guard ) { if ( p. List->Adaptor_compare(current, p. Node) == 0 ) { return current; } current = current->next; } return NULL; } And the adaptor's comparison function calls the client's comparison function: int 32_t DList. Adaptor_compare(const DNode* const left. Node, const DNode* const right. Node) { const DList. Aggregator* left. Wrapper = node. To. Wrapper(left. Node); const DList. Aggregator* right. Wrapper = node. To. Wrapper(right. Node); return Client_compare((void*) left. Wrapper->user. Data, (void*) right. Wrapper->user. Data); } [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Imposition on the Client C Linked List 16 The design of the adaptor's comparison

Imposition on the Client C Linked List 16 The design of the adaptor's comparison function imposes a constraint on the client: int 32_t DList. Adaptor_compare(const DNode* const left. Node, const DNode* const right. Node) {. . . return Client_compare((void*) left. Wrapper->user. Data, (void*) right. Wrapper->user. Data); } The client must implement a comparison function with the appropriate interface: int 32_t compare(void* p. Left, void* p. Right); [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Node Pointer to Aggregator Pointer C Linked List 17 The adaptor's compare function receives

Node Pointer to Aggregator Pointer C Linked List 17 The adaptor's compare function receives DNode pointers as parameters: int 32_t DList. Adaptor_compare(const DNode* const left. Node, const DNode* const right. Node) {. . . But, it needs to call the client's compare function, which takes (void) pointers to client data objects: int 32_t Site. Address_compare(const void* const p. Left, const void* const p. Right); . . . This is accomplished by using a static function in the adaptor implementation: . . . const DList. Aggregator* left. Wrapper = node. To. Wrapper(left. Node); const DList. Aggregator* right. Wrapper = node. To. Wrapper(right. Node); . . . [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Accessing the Aggregator C Linked List 18 Suppose we have a pointer p. Node

Accessing the Aggregator C Linked List 18 Suppose we have a pointer p. Node to a DNode inside an aggregator: p. Node prev next payload p. DA DList. Aggregator We want a pointer p. DA that points to the aggregator object. How do we get it? [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Accessing the Aggregator 19 C Linked List Suppose the aggregator is laid out in

Accessing the Aggregator 19 C Linked List Suppose the aggregator is laid out in memory as shown below: memory next user. Data. . . increasing addresses prev aggregator p. DA . . . DNode p. Node Then it appears we can set the value for p. DA by doing something like this: p. DA = (uint 8_t*)p. Node – sizeof(user. Data) … but that logic depends on the specific memory layout shown above. . . … and that is not guaranteed. The Standard leaves this to the compiler writer. [email protected] Computer Organization I © 2005 -2019 Mc. Quain

offsetof() to the Rescue! C Linked List 20 The Standard Library includes a relevant

offsetof() to the Rescue! C Linked List 20 The Standard Library includes a relevant C macro: 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). offsetof(F, member 3) offset of member 3 [email protected] member 1 member 2 member 3 member 4. . . Computer Organization I struct F { member 1; member 2; member 3; member 4; }; © 2005 -2019 Mc. Quain

So… C Linked List 21 p. Node Given the situation described before: prev next

So… C Linked List 21 p. Node Given the situation described before: prev next payload p. DA DList. Aggregator Then, the address of the aggregator object would (almost) be given by: p. Node – offsetof(DList. Aggregator, node) We just need to throw in a couple of typecasts to fix the pointer arithmetic: (DList. Aggregator*)( (uint 8_t*)(p. Node) – offsetof(DList. Aggregator, node) ) [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Node Pointer to Aggregator Pointer C Linked List 22 The adaptor uses the following

Node Pointer to Aggregator Pointer C Linked List 22 The adaptor uses the following function to compute DNode pointers as parameters: static DList. Aggregator* node. To. Wrapper(const DNode* const p. Node) { return ((DList. Aggregator*)((uint 8_t*)p. Node – offsetof(DList. Aggregator, node))); } [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Traversing the DList C Linked List 23 void traverse. List(DList* p. L) { DNode*

Traversing the DList C Linked List 23 void traverse. List(DList* p. L) { DNode* e = DList_Head(p. L); while ( (e = DList_Next(e)) != DList_End(p. L)) { // Get pointer to the "duct-tape" object from // the pointer to the DList element: Integer. DT *p = DList_Entry(e, Integer. DT, node); // Get value of payload within "duct-tape" object: int user. Data = p->payload; // do stuff with current user data element } } [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Full DList Interface C Linked List 24 Here's the full DList interface: // Create

Full DList Interface C Linked List 24 Here's the full DList interface: // Create a new, empty DList* DList_Create(int 32_t (*compare)(const DNode* const left, const DNode* const right)); // Return whether DList is empty. bool DList_Empty(const DList* const p. List); ////////////////////////// insertion fns // Insert *p. Node as first interior element of *p. List. void DList_Push. Front(DList* const p. List, DNode* const p. Node); // Insert *p. Node as last interior element of *p. List. void DList_Push. Back(DList* const p. List, DNode* const p. Node); // Insert *p. Node in ascending order according to the adaptor's // comparison function. void DList_Push. Ordered(DList* const p. List, DNode* const p. Node); . . . [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Full DList Interface C Linked List 25 . . . //////////////////////////// search fn //

Full DList Interface C Linked List 25 . . . //////////////////////////// search fn // Search list for an element equal to user-supplied data object. DNode* DList_Find(const DList* const p. List, const DNode* const p. Node); /////////////////////////// deletion fns // Remove first interior element of *p. List and return it. DNode* DList_Pop. Front(DList* const p. List); // Remove last interior element of *p. List and return it. DNode* DList_Pop. Back(DList* const p. List); // Remove element equal to user-supplied data object. DNode* DList_Remove(DList* p. List, const DNode* const p. Node); [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Full Adaptor Interface C Linked List 26 Here's the full adaptor interface: ////////////////////// adaptors

Full Adaptor Interface C Linked List 26 Here's the full adaptor interface: ////////////////////// adaptors for DList insertion fns // Prefixes user's data object (encapsulated in an aggregator) to a DList. void DList. Adaptor_Push. Front(DList* const p. List, void* const p. Data); // Appends user's data object (encapsulated in an aggregator) to a DList. void DList. Adaptor_Push. Back(DList* const p. List, void* const p. Data); // 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 DList. Adaptor_Push. Ordered(DList* const p. List, void* const p. Data); /////////////////////// adaptors for DList search fn // Searches a DList for an occurrence of a given user data object. void* DList. Adaptor_Find(const DList* const p. List, const void* const p. Data); . . . [email protected] Computer Organization I © 2005 -2019 Mc. Quain

Full Adaptor Interface C Linked List 27 Here's the full adaptor interface: . .

Full Adaptor Interface C Linked List 27 Here's the full adaptor interface: . . . //////////////////////// adaptors for DList remove fns // Removes/returns the first element in the list. void* DList. Adaptor_Pop. Front(DList* const p. List); // Removes/returns the last element in the list. void* DList. Adaptor_Pop. Back(DList* const p. List); // Removes/returns the first element in the list that matches *p. Data, // according to the user's compare function. void* DList. Adaptor_Remove(DList* const p. List, const void* const p. Data); [email protected] Computer Organization I © 2005 -2019 Mc. Quain