何uintptr_t
に使用できますか?
何uintptr_t
に使用できますか?
回答:
uintptr_t
データポインタを格納できる符号なし整数型です。これは通常、ポインタと同じサイズであることを意味します。
アーキテクチャーのポインター型を保持できる整数型が必要になる一般的な理由は、ポインターに対して整数固有の操作を実行するか、整数の「ハンドル」として提供してポインターの型を不明瞭にすることです。
編集:スティーブ・ジェソップがあなたの教育的なタイプの別の答えでいくつかの非常に興味深い追加の詳細(私が盗むことはありません)を持っていることに注意してください:)
size_t
最大のオブジェクトのサイズを保持するのに十分である必要があるだけで、ポインタよりも小さいことに注意してください。これは、8086(16ビットのようなセグメント化されたアーキテクチャ上で予想されるsize_t
が、32ビットvoid*
)
ptrdiff_t
。uintptr_t
そのためのものではありません。
unsigned int
は通常、十分な大きさではありません。しかし、それは十分に大きいかもしれません。このタイプは、特にすべての「仮定」を削除するために存在します。
質問が出されたときの最初のことuintptr_t
は、C ++ ではありませんでした。これ<stdint.h>
は、オプションの型としてC99にあります。多くのC ++ 03コンパイラはそのファイルを提供しています。また、C ++ 11のにも<cstdint>
あります。ここでも、これはオプションであり、定義ではC99を参照します。
C99では、「voidへの有効なポインターをこの型に変換でき、その後voidへのポインターに変換でき、結果が元のポインターと同じになるプロパティを持つ符号なし整数型」として定義されています。
これは、それが言っていることを意味すると考えてください。サイズについては何も言っていません。
uintptr_t
と同じサイズの場合がありvoid*
ます。大きいかもしれません。このようなC ++実装はひどいアプローチになっていますが、おそらくそれはもっと小さいかもしれません。たとえばvoid*
、32ビットの仮想プラットフォームで仮想アドレス空間が24ビットしか使用されてuintptr_t
いない場合、要件を満たす24ビットを使用できます。なぜ実装がそれを行うのかはわかりませんが、標準では許可されています。
void*
。ただし、特に、変換されたポインターではなく、整数のハンドルにすぎないものを使用するように変更したい場合は、将来の方向性に影響します。
typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;
。代わりに:callback.dataPtr = (void*)val
。反対に、もちろんそれを取得void*
してにキャストし直す必要がありint
ます。
これは、ポインタのサイズとまったく同じ符号なし整数型です。たとえば、すべてのビットを反転する(理由は問わない)など、ポインターを使用して異常なことを行う必要がある場合は常にuintptr_t
、それをキャストして通常の整数として操作してから、キャストバックします。
void*
ポインター値を変換して元のポインターにuintptr_t
戻す値を再び生成するvoid*
ことです。uintptr_t
は通常と同じサイズですがvoid*
、これは保証されていません。また、変換された値のビットが特定の意味を持つことも保証されていません。また、情報を失うことなく、関数へのポインターへの変換値を保持できるという保証はありません。最後に、それが存在することは保証されていません。
「uintptr_tデータ型とは」という部分には、すでに多くの良い答えがあります。「何のために使えるの?」に取り組みます。この投稿に含まれています。
主にポインターのビット単位の操作用。C ++では、ポインタに対してビット演算を実行できないことに注意してください。理由については、Cでポインターに対してビット単位の操作を実行できない理由を参照してください。これを回避する方法はありますか?
したがって、ポインタに対してビット単位の演算を行うには、ポインタをunitpr_t型にキャストしてからビット単位の演算を実行する必要があります。
これは、XORリンクリストに格納するためにビット単位の排他的または2つのポインターを実行するように記述した関数の例です。これにより、二重リンクリストのように双方向にトラバースできますが、各ノードに2つのポインターを格納するペナルティはありません。 。
template <typename T>
T* xor_ptrs(T* t1, T* t2)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
}
別のネクロマンサーバッジを取得するリスクを冒して、uintptr_t(またはintptr_t)の非常に優れた使用法を1つ追加します。これにより、テスト可能な埋め込みコードが作成されます。私は主に、さまざまなアームと現在テンシリカのプロセッサをターゲットにした埋め込みコードを書いています。これらにはさまざまなネイティブバス幅があり、テンシリカは実際にはハーバードアーキテクチャであり、コードバスとデータバスが異なる可能性があります。私はコードの多くにテスト駆動開発スタイルを使用しています。つまり、私が書くすべてのコードユニットに対してユニットテストを実行しています。実際のターゲットハードウェアでユニットテストを行うのは面倒なので、通常はCeedlingとGCCを使用して、WindowsまたはLinuxのIntelベースのPCですべてを書き込みます。そうは言っても、埋め込まれたコードの多くは、ビットのいじりやアドレス操作を伴います。私のIntelマシンのほとんどは64ビットです。したがって、アドレス操作コードをテストする場合は、数学を実行するための汎用オブジェクトが必要です。したがって、uintptr_tは、ターゲットハードウェアにデプロイする前に、コードをデバッグするマシンに依存しない方法を提供します。別の問題は、一部のマシンまたは一部のコンパイラのメモリモデルでさえ、関数ポインタとデータポインタの幅が異なります。これらのマシンでは、コンパイラーは2つのクラス間のキャストを許可しないこともありますが、uintptr_tはどちらも保持できるはずです。