ハンガリアン記法について
ハンガリアン記法はオワコンなのか?
そんなことはない、現時点でもgithubで検索すると、現在進行中のプロジェクトで、Linux/Windows両対応のソースコードにもまだまだ使用されている。ただ、提唱者の意図していない形になってしまったのは残念ではあるが。
ハンガリアン記法のルールは?
基本は、変数名の先頭(プレフィックス)に付けるキーワードのルールとなる。型が複数(ポインタなど)になってくると、プレフィックスも複数になってくる。
■ルール1:スコープに応じたプレフィックスをつける。
WIN32APIの仮引数で登場した記法なので、グローバル変数に関しては見たことがない。
”m_" は、今でもよく見るルールである。MFCでは、MS製ソースコードもサンプルでよく見たが、ほとんどこれが記載されていた。
- グローバル変数…"g_"をつける
- メンバー変数..."m_"をつける
- ローカル変数...以下で説明
- 関数の引数...ローカル変数のルールに準ずる。
ただ、もっとシンプルにしたいと願うのも確かである。以下のルールでも十分役目を果たせると思うが、如何だろうか。
- グローバル変数は "__" アンダーバーを2つ付ける。
- メンバー変数は "_" アンダーバーを1つ付ける。
■ルール2:ポインタの場合は "lp" ないし、"p" をつける。
今の時代に "near" だの "far" だの、意味があるのだろうかと思うわけだが、 "l" はロングのLで、意味するところは、near/far の far の方を指していたのは、はるか昔のWIN16APIの話となる。現在のVisual Studioでは、near/far は無意味である。だが、Visual Studio で "LPCVOID" と記載して、右クリックして、「定義へ移動」を選択してみて欲しい。"typedef CONST void far LPCVOID;"の行にたどり着いたと思うが、そのもうちょっと上の行にはまだ、「near」の文字が散見される。 "typedef BOOL near PBOOL;" の行を見て、「今どき "far" もないよな。」と "LP" でなく、"P" を使用した場合、もれなく "near" が付いてくることになる。 これを見て、ポインタ宣言の時は "P" ではなく、"LP" で宣言するようにしている。重ねて言うが、"near"は意味がないキーワードである。
昔(WIN16API)の名残で、"lp" と "p" の両方をWindowsのヘッダーファイルは宣言しているが、実は1つないものがある。 LPCVOIDは宣言されているが、PCVOIDはないのである。よく使いそうなのにない。これも、自作関数で "P" に統一できない理由である。
■ルール3:const などの定数の場合は、"c"をつける。
const もWindowsでは、"CONST" と宣言されているので、これを使う。
■ルール4:wide char/multi byteの両方を意識した変数の場合は "t"をつける。
UNICODEの宣言あり/なしで変数(実は関数も変わる)の型が変わる場合、プレフィックスとして "t" を付与する。 予想だが、"transparent:透過的"の t ではなかろうか。 UNICODE関数の場合は、文字数を返したり、wchar_t*型を返してくることになるので、サイズや内容操作時は注意が必要である。
■ルール5:ここからは、変数毎の命名規約になる。
ハンガリアン記法とは全く関係がないが、Windowsはプリミティブな変数に対しても、大文字で再定義しているものが多い。将来を見越しての対処だったのだと思うが、今までスムーズに16bit~32bit~64bitに遷移してこれているので、杞憂だったのではないか。しかし、ほとんどのWIN32APIはこの大文字変数型を使用しているので、これに追従していくしかない。
■■8bit変数の場合
- signed char の場合は、"c" をプレフィックスに付ける。宣言は CHAR を使う。
- unsigned char の場合は、"by" をプレフィックスに付ける。宣言は UCHAR/BYTE を使う。
変数名 | 型 |
---|---|
cSize | CHAR |
byBuffer | UCHAR/BYTE |
// winnt.h typedef char CHAR; // minwindef.h typedef unsigned char UCHAR; typedef unsigned char BYTE;
■■16bit変数の場合
- signed short の場合は、"s" をプレフィックスに付ける。宣言は SHORT を使う。
- unsigned short の場合は、"w" をプレフィックスに付ける。宣言は USHORT/WORD を使う。
変数名 | 型 |
---|---|
sSize | SHORT |
wParam | USHORT/WORD |
// winnt.h typedef short SHORT; // minwindef.h typedef unsigned short USHORT; typedef unsigned short WORD;
■■32bit変数の場合
- signed int の場合は、"i" をプレフィックスに付ける。宣言は INT を使う。
- unsigned int の場合は、"ui"をプレフィックスに付ける。宣言は UINT を使う。
- signed long の場合は、"l" をプレフィックスに付ける。宣言はLONG を使う。
- unsigned long の場合は、"ul" をプレフィックスに付ける。宣言はULONG を使う。
- unsigned long の場合は、"dw"をプレフィックスに付ける。宣言は DWORD を使う。
変数名 | 型 |
---|---|
iDataSize | INT |
uiPathLength | UINT |
lTableSize | LONG |
ulAreaSize | ULONG |
dwBufferLen | DWORD |
※Windowsは "long"の宣言でも32ビットである。逆にLinuxは "long" の宣言で64ビットとなる。
https://marycore.jp/prog/c-lang/data-type-ranges-and-bit-byte-sizes/
// minwindef.h typedef int INT; typedef unsigned int UINT; typedef unsigned long ULONG; typedef unsigned long DWORD; // winnt.h typedef long LONG;
■■64bit変数の場合
- signed long long の場合、"ll" をプレフィックスに付ける。宣言は LONGLONG を使う。
- unsigned long long の場合、"ull" をプレフィックスに付ける。宣言はULONGLONG を使う。
※32Bitの時代に生まれたハンガリアン記述のため、64Bitに関しては諸説あり、な状況である。
// winnt.h typedef __int64 LONGLONG; typedef unsigned __int64 ULONGLONG;
WIN32で64ビットを扱うために、以下の宣言を使用する場合があるが、これはユニオンである。
LARGE_INTEGER は、ユニオン(_LARGE_INTEGER)をtypedef宣言したもので、以下のようになっている。
typedef union _LARGE_INTEGER { struct { DWORD LowPart; LONG HighPart; } DUMMYSTRUCTNAME; struct { DWORD LowPart; LONG HighPart; } u; LONGLONG QuadPart; } LARGE_INTEGER;
同様にULARGE_INTEGER も、以下のように宣言されている。
typedef union _ULARGE_INTEGER { struct { DWORD LowPart; DWORD HighPart; } DUMMYSTRUCTNAME; struct { DWORD LowPart; DWORD HighPart; } u; ULONGLONG QuadPart; } ULARGE_INTEGER;
で、LowとHighをどう使い分けるのか?
これは、入れる場合と、出す場合を統一すれば、問題ない。ここであえて、エンディアンを考える必要はない。
もし、Intel系のビッグエンディアンで作成して、非Intel系のリトルエンディアンで取り出す場合を想像しているなら、それは16ビットの変数から検討しなければならない。
■■実数の場合
※なぜ、doubleに対して"DOUBLE"を用意しなかったのか?
変数名 | 型 |
---|---|
fLeftPos | FLOAT |
dMaxWidth | double |
■■bool変数の場合
- boolの場合は、"b"をプレフィックスに付ける。宣言はBOOLを使用する。
変数名 | 型 |
---|---|
bShowWindow | BOOL |
なお、BOOLはint型である。
// minwindef.h typedef int BOOL;
■■void型の場合
- voidの場合は、"v"をプレフィックスに付ける。宣言はVOIDを使用する。
変数名 | 型 |
---|---|
lpvParam | LPVOID |
宣言で使用するVOIDは主にアドレスの取得として用いられる。32Bit/64Bitの影響をモロに受ける変数であるが、そもそもサイズ感のない変数なので32→64に換装する場合は要注意変数である。
■■文字列の場合
文字列の場合、基本的には「ヌルターミネーション」であることが、C/C++の標準なので、一位にプレフィックス"sz"が決まる。
ちなみに、string zero termination の省略形である。
通常の文字列とは違う形式としてBSTR型が存在する。BSTR型の文字列の持ち方は特殊で、先頭に文字列長、その後に文字列、という持ち方をしている。この変数の場合、NULLで終端を閉じていないため、'z'は付かないことになる。
変数名 | 型 |
---|---|
szPath | char [ ] |
tszPath | wchar_t [ ] |
■WIN32APIによく登場する変数宣言
変数名 | 型 | cSize | char/CHAR |
---|---|
sParam | short/SHORT |
iCounter | int/INT |
lpvParam | void* |
lpcszFilePath | char*/CHAR* |
lpctszFilePath | const char*/CONST CHAR* |
szBuffer | char [ ] |
tszBuffer | wchar_t [ ] |
cSize sParam iCounter lpvParam lpcszFilePath lpctszFilePath