linked lists - virginia techc linked list computer organization i 1 cs@vt ©2005-2019 mcquain linked...
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);