I was looking over the source code for the "tfind" function from the "search.h" C library and I stumbled upon this line:
#define DEREFNODEPTR(NP) (node)((uintptr_t)(*(NP)) & ~((uintptr_t) 0x1))
This is how it's used:
/* Find datum in search tree.
KEY is the key to be located, ROOTP is the address of tree root,
COMPAR the ordering function. */
void *
__tfind (const void *key, void *const *vrootp, __compar_fn_t compar)
{
node root;
node *rootp = (node *) vrootp;
if (rootp == NULL)
return NULL;
root = DEREFNODEPTR(rootp);
CHECK_TREE (root);
while (DEREFNODEPTR(rootp) != NULL)
{
root = DEREFNODEPTR(rootp);
int r;
r = (*compar) (key, root->key);
if (r == 0)
return root;
rootp = r < 0 ? LEFTPTR(root) : RIGHTPTR(root);
}
return NULL;
}
So, why is one's complement needed in this case?
Thank you for your time and consideration.
It is likely the case that the implementation assumes the node pointers are to nodes that are always at least 2 byte aligned. This means that the least significant bit will always be 0 for a valid node pointer. This allows the bit to be "used" to store state (such as whether or not it has been visited before, or whether it is a red or black node, or some other thing...).
The macro clears the LSB before accessing the value as a pointer. There is no one's complement requirement.