web-dev-qa-db-ja.com

Cでの型キャストに `intptr_t`を使用する理由とタイミング

intptr_tlong intの使用に関して質問があります。私は、メモリアドレスのインクリメント(たとえば、手動ポインタ演算による)がデータ型によって異なることを観察しました。たとえば、charポインタをインクリメントするとメモリアドレスに1が加算されますが、intポインタをインクリメントするとdoubleに4、8、long doubleに16などが加算されます。

最初に私はこのようなことをしました:

char myChar, *pChar;
float myFloat, *pFloat;

pChar = &myChar;
pFloat = &myFloat;

printf( "pChar:  %d\n", ( int )pChar );
printf( "pFloat: %d\n", ( int )pFloat );

pChar++;
pFloat++;

printf( "and then after incrementing,:\n\n" );
printf( "pChar:  %d\n", (int)pChar );
printf( "pFloat:    %d\n", (int)pFloat );

コンパイルと実行は問題ありませんでしたが、XCodeは型キャストに対して「ポインターから異なるサイズの整数へのキャスト」という警告を出しました。

いくつかのグーグルとビンギングの後(後者はまだWordですか?)、一部の人々がintptr_tの使用を推奨するのを見ました:

#include <stdint.h>

...

printf( "pChar:  %ld\n", ( intptr_t )pChar );
printf( "pFloat: %ld\n", ( intptr_t )pFloat );

実際にエラーを解決します。ですから、今後、型キャストポインタにintptr_tを使用する必要があると考えました...しかし、少し調整した後、intlong intに置き換えるだけで問題を解決できることがわかりました。

printf( "pChar:  %ld\n", ( long int )pChar );
printf( "pFloat: %ld\n", ( long int )pFloat );

だから私の質問は、なぜintptr_tが便利で、いつ使用する必要があるのか​​ということです。この例では不要なようです。明らかに、myCharmyFloatのメモリアドレスが大きすぎてint...に収まらないので、それらをlong intsに型キャストすると問題が解決しました。

long intに対してもメモリアドレスが大きすぎる場合がありますか?考えてみると、4 GBを超えるRAMがあれば可能だと思います。この場合、メモリアドレスは2 ^ 32-1(符号なしlong intの最大値...)を超える可能性がありますが、Cはずっと前に作成されました想像できるでしょう?それとも、それらは先見の明がありましたか?

ありがとう!

43
grisaitis

これは、一部のプラットフォームではintが適切なサイズですが、他のプラットフォームではlongが適切なサイズです。どれを使用すべきかをどのようにして知るのですか?あなたはしません。 1つは正しいかもしれませんが、標準は、どちらになるかについて保証しません(どちらかである場合)。そのため、標準では、使用しているプラ​​ットフォームに関係なく、正しいサイズであると定義されたタイプが提供されます。あなたが書く必要があった前の場所:

#ifdef PLATFORM_A
  typedef long intptr;
#else
  typedef int intptr;
#endif

今、あなたは書くだけです:

#include <stdint.h>

そして、さらに多くのケースをカバーしています。 すべての単一プラットフォームでコードを実行するために上記のスニペットを特化することを想像してください。

36
Chris Lutz

intptr_tは、64ビットおよび128ビットのメモリアドレスが想像された後に作成された新しい発明です。

everポインタを整数型にキャストする必要がある場合、always use intptr_t。他に何かをすることは、将来あなたのコードを移植する必要がある人々にとって不必要な問題を引き起こすでしょう。

人々が64ビットLinuxでコンパイルしたいと思ったとき、Mozilla/Firefoxのようなプログラムでこれに関するすべてのバグを解決するのに長い時間がかかりました。

45
Zan Lynx

まず、intptr_tはデータポインター専用であり(関数ではない)、存在が保証されていません。

それから、いや、印刷の目的でそれを使うべきではありません。 %pはそのためのものです。ポインタを(void*)にキャストするだけです。

また、算術/個々のバイトへのアクセスにも適していません。代わりに(unsigned char*)にキャストしてください。

intptr_tは、ポインターを整数として解釈しなければならないまれな場合のためです(実際にはそうではありません)。する必要がない場合は、そうしないでください。

10
Jens Gustedt

p変換指定子を使用すると、生活が楽になります。

printf("%p\n", (void *)foo);

また、タイプ(u)intptr_tの変数を印刷するポータブルな方法は、PRI*PTRinttypes.hマクロを使用することです。以下は、私のプラットフォーム(32ビット)でpを使用するのと同等です。

printf("%08" PRIxPTR "\n", (uintptr_t)(void *)foo);

void *へのキャストは完全な移植性のために必要ですが、統一されたポインター表現を持つプラットフォームでは省略できます。

9
Christoph