pointer hashing
memory management
computer science
data structures
programming techniques

Hashing of pointer values

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Hashing a pointer means turning a memory address into a hash code that can be used in structures such as hash tables. That sounds simple, but the important question is what you are trying to identify: the object by its address, or the value stored inside the object.

Pointer Identity and Object Value Are Different

If you hash a pointer directly, you are hashing identity. Two pointers hash the same when they refer to the same address. That is useful for caches, node registries, and graph algorithms where object identity matters.

If you want two separate objects with equal contents to behave as equal keys, then hashing the pointer value is the wrong strategy. In that case, you should hash the pointed-to data instead.

The distinction shows up immediately in C++:

cpp
1#include <iostream>
2#include <string>
3
4struct User {
5    std::string name;
6};
7
8int main() {
9    User a{"sam"};
10    User b{"sam"};
11
12    std::cout << (&a == &b) << '\n'; // 0
13    std::cout << (a.name == b.name) << '\n'; // 1
14}

The objects contain the same value, but they have different addresses. A pointer hash would treat them as different keys.

Using Standard Library Pointer Hashing

In modern C++, std::hash<T*> already exists. For many cases, that is enough.

cpp
1#include <iostream>
2#include <string>
3#include <unordered_set>
4
5struct Node {
6    std::string id;
7};
8
9int main() {
10    Node a{"A"};
11    Node b{"B"};
12
13    std::unordered_set<Node*> seen;
14    seen.insert(&a);
15    seen.insert(&b);
16    seen.insert(&a);
17
18    std::cout << seen.size() << '\n'; // 2
19}

This works because the hash is based on the address, and equality is also based on the address. For identity-based containers, the standard behavior is usually correct and clear.

Why Custom Mixing Can Still Matter

Raw addresses often have predictable low bits because of alignment. If objects are aligned to 8 or 16 bytes, those low bits are frequently zero, and a naive hash can distribute poorly if the hash table implementation does not compensate well.

When you need a custom pointer hasher, convert the pointer to std::uintptr_t and mix the bits before returning a std::size_t.

cpp
1#include <cstdint>
2#include <iostream>
3#include <unordered_map>
4
5struct Widget {
6    int value;
7};
8
9struct PointerHash {
10    std::size_t operator()(const Widget* ptr) const noexcept {
11        std::uintptr_t x = reinterpret_cast<std::uintptr_t>(ptr);
12
13        x ^= x >> 33;
14        x *= 0xff51afd7ed558ccdULL;
15        x ^= x >> 33;
16        x *= 0xc4ceb9fe1a85ec53ULL;
17        x ^= x >> 33;
18
19        return static_cast<std::size_t>(x);
20    }
21};
22
23int main() {
24    Widget a{1};
25    Widget b{2};
26
27    std::unordered_map<Widget*, int, PointerHash> counts;
28    counts[&a] = 10;
29    counts[&b] = 20;
30
31    std::cout << counts[&a] << '\n';
32}

The exact mixing function is less important than the principle: do not assume raw addresses are already well distributed.

Lifetime and Portability Concerns

Pointer hashes are only meaningful while the pointed-to objects remain alive. Once an object is destroyed, the address may later be reused, and a stale hashed pointer becomes dangerous.

There are a few practical implications:

  • never persist pointer hashes to disk as stable identifiers
  • never compare pointer hashes across processes
  • do not assume the same address will exist after restart
  • remember that address space layout randomization changes addresses between runs

If you need a stable key, generate one explicitly, such as a numeric ID or UUID.

Common Pitfalls

  • Hashing the pointer when you meant to hash the data behind it. That creates identity semantics instead of value semantics.
  • Storing pointers to objects whose lifetime is not guaranteed. A container full of dangling pointers is a correctness bug, not a hashing problem.
  • Assuming raw addresses are uniformly distributed. Alignment can reduce entropy in predictable bit positions.
  • Treating pointer hashes as portable or persistent. They are process-local implementation details.

Summary

  • Hashing a pointer usually means hashing object identity, not object contents.
  • 'std::hash<T*> is often enough for identity-based hash tables in C++.'
  • Custom hashers should mix pointer bits instead of relying on raw addresses directly.
  • Pointer hashes are valid only while the objects are alive in the current process.
  • Use explicit stable IDs when you need persistence or cross-process comparison.

Course illustration
Course illustration

All Rights Reserved.