数値型の別名定義を用意しているプロジェクトは多いです。
1 2 3 4 5 |
typedef int s32 typedef unsigned int u32 typedef char s8 typedef unsigned char u8 // 以下各型それぞれに続く |
符号付か否か、あとは型の持つbit数などを踏まえたエイリアスを用意することでタイピング数の削減と統一的な名前規則でコードを分かりやすくしようという意図が感じられます。
一定規模以上のプロジェクトになると、ほぼ必ず上記のような型の再定義ファイルがあるように感じます。既視感のある人も多いのではないでしょうか。
ただ、上記の例は非常にポータビリティに欠ける為、行うべきではありません。
ユーザーが「intの別名はs32」と定義したところで、実際にs32のサイズが32bitになる保証はどこにもありません。
サイズが保証された数値型を標準が用意してくれているので、そちらを使うようにしましょう。
1 2 3 4 5 |
#include <cstdint> std::int8_t // 8bit符号付き整数 std::uint32_t // 32bit符号無し整数 // などなど 8bit - 64bitまでのビットサイズが保証された符号付/無整数型 |
どんな環境でもstd::int8_tは8bit符号付き整数を表すことが保証されています。
アドレス型のサイズとintのサイズが違う環境も多いので、アドレスを数値として計算する場合は気をつけましょう。例えば、64bitCPUの場合、アドレスの値の範囲は64bitですがintのサイズは32bitというケースも多いです。
1 |
int diff = reinterplet_cast<int>(&obj_a) - reinterplet_cast<int>(&obj_b); |
obj_aのアドレスとobj_bのアドレスの差分を計算するコードですが、例えばポインタが64bitなのにintは32bitだった場合、サイズオーバーのため正しい計算結果が得られない可能性が高いです。
なのでポインタ(アドレス)を数値として計算する場合は、ちゃんとアドレスと同じ値の幅を持つことが保証されている数値型std::intptr_t、もしくはstd::uintptr_tを使いましょう。
1 |
std::intptr_t diff = reinterplet_cast<std::intptr_t>(&obj_a) - reinterplet_cast<std::intptr_t>(&obj_b); |
あなたが一生懸命作ったスマートフォン向けのゲームを3DSに移植したら全然動かなくなっちゃった!とならないためにも日頃からポータビリティを意識したコードを書くようにしましょう。
まとめ
- 値の幅が保証された標準の数値型を使おう
- アドレスの計算にはアドレスの幅が保証された標準の数値型を使おう