64bit OS と 32bit OS でのデータ型の相違一覧
Perl-XS なモジュールを 64bit OS で使っていると、ごく希に変数型サイズが問題で不具合を経験することがあります。例えば、メジャーどころで言えば、日本語係り受け解析器のCaboCha/南瓜を例に挙げますと、
src/common.h の103〜107行目
static inline void replaceString (std::string &s, const std::string &src, const std::string &dst)
{
unsigned int pos = s.find (src);
if (pos != std::string::npos) s.replace (pos, src.size(), dst);
}
の unsigned int pos = s.find (src); の部分ですが、32bit 環境では問題は発生しませんが、64bit 環境ではポインタ型のサイズが異なって正常動作しなくなります。正しくは、STL(Standard Template Library) の仕様通り、size_t で受け取る必要があって、
static inline void replaceString (std::string &s, const std::string &src, const std::string &dst)
{
size_t pos = s.find (src);
if (pos != std::string::npos) s.replace (pos, src.size(), dst);
}
になるわけです。(これはかなり前のネタですが、既に作者の方とやりとりさせて頂いています)。
その他、CPAN にあがっているもので記憶にあるのは暗号モジュールの、Crypt-Rijndael-0.05 も 64bit で不具合がありました。具体的には、
rijndael.h の 30〜31 行目
typedef unsigned long UINT32;
typedef unsigned char UINT8;
の typedef unsigned long UINT32; が不具合の原因です。64bit 環境では long 型は 8byte なので UINT32 つまり 4byte では無くなるわけです。
このような不具合を出さないためには、まず変数型のサイズを把握しておく必要があります。
自分の環境で変数のサイズを知るのは簡単で、次のプログラムをコンパイルして実行すればOKです。
#include <stdio.h>
int main(void)
{
printf("char : %d\n", sizeof(char));
printf("short : %d\n", sizeof(short));
printf("int : %d\n", sizeof(int));
printf("long : %d\n", sizeof(long));
printf("float : %d\n", sizeof(float));
printf("double: %d\n", sizeof(double));
printf("long long : %d\n", sizeof(long long));
printf("long double: %d\n", sizeof(long double));
return 0;
}
ちなみに、x86 系 Linux の 32bit と 64bit での結果はこんな感じになります。黄色の部分がサイズが異なる部分です。
変数の型 | 32bit | 64bit |
---|---|---|
char | 1 byte | 1 byte |
short | 2 byte | 2 byte |
int | 4 byte | 4 byte |
long | 4 byte | 8 byte |
float | 4 byte | 4 byte |
double | 8 byte | 8 byte |
long long | 8 byte | 8 byte |
long double | 12 byte | 16 byte |
もっともこれは x86 系 Linux の 32bit と 64bit の比較をしただけなので、Sparc 系の cpu では違った結果になると思います。ポインターを操作する場合は、環境依存しないように sizeof 関数でサイズを取得して、size_t 型の変数で値を受け取って操作するのがベターと思います。
コメントやシェアをお願いします!