用指針低三位存放額外資訊的優化方法
一種用指針低三位存放額外資訊的優化方法
在8位元組對齊的情況下指針的低三位都是0,在某些情況下我們需要維持跟指針對應的額外資訊,例如標誌位,但是又不想為此創建一個結構體,那就可以利用指針的低三位來存儲額外資訊。
比如說我們希望原子操作的類型是64位的數據,這樣可以利用CMPXCHG機器指令實現CAS操作,即我們希望定義std::atomic<T *>
的變數實現原子操作,而不希望定義std::atomic<S>
,S為某個結構體。這時候就可以應用上述技巧。
具體實現方式如下所示:
std::uintptr_t
是定義一個可以表示地址值(指針值即地址值)的無符號整型。將指針轉換成std::uintptr_t
然後進行位運算。以下程式碼作為示例:
#include <iostream>
#include <stdint.h>
struct Data
{
// 定義一些數據成員
long int a;
long int b;
long int c;
};
// 定義三個標誌位,不用細究三個標誌位的具體含義,這個在這裡不重要。
static constexpr std::uintptr_t HAS_DATA = 1;
static constexpr std::uintptr_t NEED_DATA = 2;
static constexpr std::uintptr_t CLOSED = 4;
static constexpr std::uintptr_t FLAGS_MASK = HAS_DATA | NEED_DATA | CLOSED;
static constexpr std::uintptr_t PTR_MASK = ~FLAGS_MASK;
int main()
{
Data * dp = new Data();
dp->a = 88;
dp->b = 99;
dp->c = 77;
std::cout << "指針值為 " << std::hex << reinterpret_cast<int64_t>(dp) << std::endl;
// 在指針上附加上標誌位
std::uintptr_t ptr_int = reinterpret_cast<std::uintptr_t>(dp) | HAS_DATA;
std::cout << "加過標記位後 " << std::hex << ptr_int << std::endl;
// 取標誌位
std::uintptr_t flags = ptr_int & FLAGS_MASK;
std::cout << "標記位 " << flags << std::endl;
// 需要用指針的時候,清零低三位,恢復指針值原來的值
dp = reinterpret_cast<Data*>(ptr_int & PTR_MASK);
std::cout << "使用恢復後的指針" << std::dec << dp->a << ", " << dp->b << ", " << dp->c << std::endl;
return 0;
}