Folioscope

プログラミング/Unix系/デザイン/CG などのメモがもりもり

ポインタ変数に付加機能をつける

変数はアライメントといって, ある一定の倍数のアドレスに変数領域が確保されます.
アライメントは変数のサイズと同じことが多いですが, そう定義されているわけでもなく, 例外もあります.
アライメントについては次のサイトが詳しいです.
http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html


例えばアライメントが4の場合, 変数のアドレスは4の倍数となります.
そのため変数のアドレスは下位2ビットが常に0となります.
そこでこの下位2ビットに自由な情報を付加してみます.


今回はアドレスを格納するクラスを作成します.
そしてauto_ptrのようにポインタ変数ライクなメソッドを実装します.
ここではポインタに対してバイナリ演算子(|, &)が使えなかったためとりあえずu_int64_tにキャストしていますが, 本来はCPUアーキテクチャに合わせるべきです.

template <typename T>
class ExPointer {
public:
  ExPointer(T *p) : pt(p) {}
  operator T*() { return reinterpret_cast<T*>(reinterpret_cast<u_int64_t>(pt) & ~u_int64_t(3)); }
  const T operator*() { return *operator T *(); }
  bool flag1() const { return reinterpret_cast<u_int64_t>(pt) & 1 << 0; }
  bool flag2() const { return reinterpret_cast<u_int64_t>(pt) & 1 << 1; }
  void setFlag1(bool b) { pt = reinterpret_cast<T*>(reinterpret_cast<u_int64_t>(pt) | b << 0); }
  void setFlag2(bool b) { pt = reinterpret_cast<T*>(reinterpret_cast<u_int64_t>(pt) | b << 1); }
private:
  T *pt;
};

そしてこのクラスの使用例は次のようになります.
print()関数ではflag1が立っているとマイナス値を, flag2が立ってると16進表示をします.

void print(ExPointer<int> &v) {
  int value = *v;
  if (v.flag1()) value = -value;
  if (v.flag2()) {
    printf("%x\n", value);
  } else {
    printf("%d\n", value);
  }
}

int main() {
  int num = 123;
  ExPointer<int> pint1 = &num;
  ExPointer<int> pint2 = &num;
  ExPointer<int> pint3 = &num;
  ExPointer<int> pint4 = &num;

  pint2.setFlag1(true);
  pint3.setFlag2(true);
  pint4.setFlag1(true);
  pint4.setFlag2(true);

  print(pint1);    // 123
  print(pint2);    // -123
  print(pint3);    // 7b
  print(pint4);    // ffffff85

  return 0;
}

もちろん通常のint*へのキャストもお手の物.

  int *p = pint1;
  printf("%d\n", *p);    // 123

傍注

今回はintがアライメントが4以上である事を前提とした実装です.
intのアライメントは処理系によりますし, charやboolなどの小さい変数の場合アライメントが1, つまりアライメントがされない場合が多いです.
そのためこの実装は非常に危険となります.


ただしmalloc/newなどで確保される変数には有効に使えるかもしれません.
http://www5d.biglobe.ne.jp/~noocyte/Programming/MallocAlignment.html