32ビットマシンでコンパイルして正常に動作するパッケージがあります。私はそれを64ビットマシンでコンパイルして、次のエラーを見つけようとしています-
error: cast from ‘void*’ to ‘int’ loses precision
これらのエラーを抑制するコンパイラフラグはありますか?または、これらのキャストを回避するためにこれらのファイルを手動で編集する必要がありますか?
問題は、32ビットでは、int(32ビット整数)がポインター値を保持することです。
64ビットに移動すると、ポインターをintに格納できなくなります。64ビットポインターを保持するのに十分な大きさではありません。 intptr_t タイプはこのために設計されています。
コードが壊れています。コンパイラーからの警告を無視しても、問題が解消されることはありません。
64ビット幅のポインターを32ビット整数に格納しようとすると、thinkはどうなりますか?データの半分が破棄されます。それが正しいことである場合、またはそれがエラーを引き起こさない場合は、多くの場合を想像することはできません。
コードを修正します。または、コードが現在動作している32ビットプラットフォームにとどまります。
コンパイラがintptr_t
またはuintptr_t
を定義している場合は、それらを使用します。これらは、ポインタを格納するのに十分な大きさが保証されている整数型であるためです。
これらのタイプが利用できない場合、size_t
またはptrdiff_t
も、most(すべてではない)プラットフォームでポインタを保持するのに十分な大きさです。またはlong
(GCCコンパイラの64ビットプラットフォームでは通常64ビット)またはlong long
(ほとんどの(ただしすべてのコンパイラではない)C++でサポートされるC99タイプ)、またはその他64ビットプラットフォームで少なくとも64ビット幅の実装定義の整数型。
私の推測では、OPの状況は、void *がintの一般的なストレージとして使用されており、void *がintより大きい場合です。だから例えば:
int i = 123;
void *v = (void*)i; // 64bit void* being (ab)used to store 32bit value
[..]
int i2 = (int)v; // we want our 32bits of the 64bit void* back
コンパイラーはその最後の行を好きではありません。
この方法でvoid *を乱用することが正しいか間違っているかを検討するつもりはありません。コンパイラをだます必要がある場合は、-Wallを使用しても、次の手法が機能するようです。
int i2 = *((int*)&v);
ここでは、vのアドレスを取得し、そのアドレスを必要なデータ型のポインターに変換してから、ポインターを追跡します。
理由は次のとおりです。int
は、マシンのvoid*
の半分の大きさなので、int
にvoid*
を格納することはできません。ポインタの半分を失うと、プログラムが後でそのint
から再びポインタを取得しようとしたときに、何も役に立たなくなります。
コンパイラがエラーを出さなくても、コードはおそらく機能しません。 64ビットの互換性のために、コードを変更して確認する必要があります。
ポインタをintにキャストすることは、移植性の観点からは恐ろしいことです。 intのサイズは、コンパイラとアーキテクチャの組み合わせによって定義されます。これがstdint.hヘッダーが作成された理由であり、多くの異なるWordサイズを持つ多くの異なるプラットフォームで使用している型のサイズを明示的に示すことができます。
Uintptr_tまたはintptr_tにキャストした方がよいでしょう(stdint.hから、必要な署名に最も一致するものを選択してください)。
intptr_t
コールバックなどのポインタキャストが必要なintの代わりに、最高の移植性を実現します。
これらのエラーはコードロジックの問題を示している可能性が高いため、これらのエラーを抑制したくありません。
エラーを抑制すれば、しばらくはうまくいくかもしれません。ポインタが最初の4 GBのアドレスを指している間、上位32ビットは0になり、データが失われることはありません。しかし、4 GBを超えるアドレスを取得すると、コードは「不思議なことに」機能しなくなります。
あなたがすべきことは、intptr_tへのポインタを保持できるintを変更することです。
バグが多く移植性がないと思われるコードに置き換えるには、ファイルを手動で編集する必要があります。
警告を抑制するのは悪い考えですが、コンパイラとアーキテクチャに応じて、64ビット整数を使用するコンパイラフラグがあるmayがあります。これは問題を修正する安全な方法です(もちろん、このコードは、intが32ビットであるとは想定していませんでした)。 gccの場合、フラグは-m64です。
最善の答えは依然としてコードを修正することだと思いますが、それがレガシーサードパーティコードであり、これらの警告が横行している場合、このリファクタリングが時間の非常に効率的な使用であるとは思えません。ただし、新しいコードでは、ポインターをintにキャストしないでください。
現在のC++標準で定義されているように、ポインターを保持することが保証されている整数型はありません。一部のプラットフォームにはintptr_tがありますが、これはC++の標準機能ではありません。基本的に、ポインターのビットを整数であるかのように扱うことは、移植性のあることではありません(多くのプラットフォームで機能させることはできます)。
キャストの理由がポインターを不透明にすることである場合、void *はすでにこれを実現しているため、コードはintの代わりにvoid *を使用できます。 typedefはこれをコードで少し良くするかもしれません
typedef void * handle_t;
キャストの理由がバイト単位でポインター演算を行うことである場合、おそらく(char const *)にキャストしてそれを使って計算を行うのが最善の方法です。
キャストの理由が、変更できない既存のライブラリ(おそらく古いコールバックインターフェイス)との互換性を実現するためである場合、そのライブラリのドキュメントを確認する必要があると思います。ライブラリが(64ビットプラットフォームでも)必要な機能をサポートできる場合、そのドキュメントは目的のソリューションに対応している場合があります。
私も同様の問題に直面しました。私は次の方法でそれを解決しました:
#ifdef 64BIT
typedef uint64_t tulong;
#else
typedef uint32_t tulong;
#endif
void * ptr = NULL; //Whatever you want to keep it.
int i;
i = (int)(tulong)ptr;
問題は、ポインタをより短いデータ型に型キャストすることだと思います。ただし、int
のより大きな型の場合、正常に機能します。
この問題をlong
へのポインターの型キャストから64ビット整数から32ビット整数への型キャストに変換すると、問題なく動作しました。私はまだGCC/Clangのコンパイラオプションを探しています。
64ビットのアイテムを2つの32ビットのアイテムに分割したい場合があります。これはあなたがそれをする方法です:
ヘッダーファイル:
//You only need this if you haven't got a definition of UInt32 from somewhere else
typedef unsigned int UInt32;
//x, when cast, points to the lower 32 bits
#define LO_32(x) (*( (UInt32 *) &x))
//address like an array to get to the higher bits (which are in position 1)
#define HI_32(x) (*( ( (UInt32 *) &x) + 1))
ソースファイル:
//Wherever your pointer points to
void *ptr = PTR_LOCATION
//32-bit UInt containing the upper bits
UInt32 upper_half = HI_32(ptr);
//32-bit UInt containing the lower bits
UInt32 lower_half = LO_32(ptr);