2011-12-23 4 views
17

整数値をメモリの観点からvoid*またはviceversaに変換することは何を意味しますか? 私の理解はvoid*は不特定の長さの記憶のブロックへのアドレスです。
これは、リンゴとオレンジを比較するような感じです。intをvoid *またはその逆に変換することは何を意味しますか?

int myval = 5; 
void* ptr = (void*)myval; 
printf("%d",(int)ptr); 

私は、これが使用されている正確な文脈を与えておくべきであることを認識しました。

int main(int argc, char* argv[]) { 
long  thread; /* Use long in case of a 64-bit system */ 
pthread_t* thread_handles; 

/* Get number of threads from command line */ 
if (argc != 2) Usage(argv[0]); 
thread_count = strtol(argv[1], NULL, 10); 
if (thread_count <= 0 || thread_count > MAX_THREADS) Usage(argv[0]); 

thread_handles = malloc (thread_count*sizeof(pthread_t)); 

for (thread = 0; thread < thread_count; thread++) 
    pthread_create(&thread_handles[thread], NULL, Hello, (void*) thread); 

printf("Hello from the main thread\n"); 

for (thread = 0; thread < thread_count; thread++) 
    pthread_join(thread_handles[thread], NULL); 

free(thread_handles); 
return 0; 
} /* main */ 

/*-------------------------------------------------------------------*/ 
void *Hello(void* rank) { 
long my_rank = (long) rank; /* Use long in case of 64-bit system */ 

printf("Hello from thread %ld of %d\n", my_rank, thread_count); 

return NULL; 
} /* Hello */ 

このコードは、パラレルプログラミングに関するPeter Pachechoの本からのものです。

+0

可能な複製:http://stackoverflow.com/questions/3568069/is-it-safe-to-cast-an-int-to-void-pointer-and-back-to-int-again –

+1

少なくともリンゴをジャガイモと比較するのと同じように。 – alk

答えて

17

キャストintvoid *は意味がありません。ポインターに非ポインターをキャストしようとすると、意味がありません。 C99 standardセクション6.3.2.3アイテム5を引用する:

整数は、任意のポインタ型に変換できます。以前は が指定されていた場合を除き、結果は実装定義であり、 は正しく整列されておらず、参照された 型のエンティティを指していない可能性があり、トラップ表現である可能性があります。

あなたでしキャストint *(任意のポインタはあなたがすべてのポインタの「基本型」のように考えることができますvoid *に変換可能である)void *します。 intvoid *鋳造

はポータブルではなく、(例えばvoid *多分64ビット幅とintのみ32ビットであってもよい)、使用プラットフォームに応じて完全に間違っているかもしれません。 C99標準を再度引用すると、6.3.2.3項6:

ポインタ型は整数型に変換される可能性があります。先に指定した を除き、結果は実装定義です。 の結果を整数型で表現できない場合は、動作は 未定義です。結果は任意の 整数型の値の範囲内にある必要はありません。

これを解決するために、uintptr_tを提供するプラットフォームでは、ポインタを適切な幅の数値として扱うことができます。

3

void*ポインタ(またはそれに関するポインタ)とintは、おおまかには数字です。それらは異なるビットサイズであるかもしれませんが、ポインタがintより小さくなることはありません。そのため、操作は元に戻せます。もちろん、それは違法であり、あなたがポインターを指し示す有効な位置を持たないポインターを決して逆参照すべきではありません。

2

これは、リンゴとオレンジの比較に非常に似ています。このコードが動作する唯一の方法は、明示的に前後にキャストしているからです。

Cコンパイラはint型の文字列を保持する方法と同様に、int型からポインタ型と空白型の領域にバイトをコピーするだけです。

何らかの理由で 'int'がプラットフォーム上の 'void *'よりも長い場合、これによって数字が混乱することさえあります。

整数からポインタへと逆変換する場合は、intptr_tを参照してください。この型は、実際には整数とポインタの両方を格納するように設計されています。

1

ポインター演算のためにポインターを整数型にキャストできると便利です。たとえば、構造内のメンバのオフセットを計算するoffsetof()マクロは、この種の変換を必要とします。

しかし、このために使用されるプリミティブ型がポインタを扱うことができるようにする必要があります。ここでは64 Linuxの場合、gccを使用する場合はそうではありません。void *のサイズは8、intはサイズ4.

offsetofマクロは、このような次のように定義されます

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 

これは、Linuxカーネルの二重リンクリストの実装に目立つように使用されている、非常に簡単に定義のように:

struct list_head { struct list_head *prev, *next; } 

この構造体は、同様に、カーネル構造内に埋め込まれている。

struct something { 
    //... 
    struct list_head list; 
    //... 
} 

リストを歩行、コードは、符号化構造体へのポインタを取得する必要があります。

#define container_of(ptr, type, member) \ 
    (type *)((char *)ptr - offsetoff(type, member)) 
#define list_entry(list, type, member) \ 
    container_of(list, type, member) 

とコードで、あなたは非常に頻繁に表示されます:

単にマクロとポインタ算術演算のこの種なしでなんとかならないでしょう
struct something *s; 
list_for_each(listptr, somehead) { 
    s = list_entry(listptr, struct something, list); 
    //... 
} 

。これは、この(単純化の定義)として行われますしかし、それはツールチェーンに大きく依存しています。

+0

これは 'offsetof'と何が関係しているのですか?また、 'offsetof'はプラットフォーム定義であり、コンパイラの実装は自由に実装することができます。たとえば、gccはこれを組み込み関数で実装します。 –

1

比較をする試みがあれば、それはリンゴとオレンジを比較するようなものです。しかし、そこにはありません。基本的に、そこにある大部分のアーキテクチャでは、情報を失うことなくintをvoidポインターにキャストすることができ、voidポインターをintにキャストして、情報を失うこともありません。もちろん、意味のあるメモリ位置を指しているわけではないので、そのポインタの逆参照を試みるべきではありません。それは、キャストの前に含まれていた整数が使用していたビットパターンと同じビットパターンを含む変数です。

+0

情報の損失がないことは間違いありません。 C99の標準では、「任意のポインタ型は整数型に変換される可能性がありますが、前に指定した場合を除いて、結果はインプリメンテーションによって定義されます。任意の整数型の値の範囲内にあります。 – bobbymcr

+0

申し訳ありませんが、私は私の答えを編集し、その誤りを訂正しました。 (私はC99についてのビットを編集し、「大多数」という言葉では「広大な」という言葉を編集しました) –

+0

「大多数」は今日でもおそらく偽であり、将来的にはそれほど多くはないでしょう。現代のアーキテクチャーは、主に64個のポインターと32個の「int」で構成されています。 –

3

C標準では、void型ポインタを整数型に変換して、整数型をvoid型ポインタに戻すと同じポインタを返すことができるように指定しています。私は、nullポインタを整数に変換すると値がゼロになることが必要なのか、数字のゼロの文字だけが特別なものとして認識されるのか分からない。多くの実装では、整数値はハードウェアアドレスを表しますが、標準ではそのような保証はありません。ハードウェアアドレス0x12345678の特別なトラップを含むハードウェアでは、ポインタを整数に変換するとハードウェアアドレスから0x12345678が減算され、整数をポインタに変換すると0x12345678が戻される可能性があります(したがって、ゼロはヌルポインタを表します)。

多くの場合、特にエンベデッドコントローラ用に開発する場合、特定の整数値をポインタ型に変換するときに、どのハードウェアアドレスにアクセスするかを明示的に指定します。単一のリニアアドレス空間を持つプロセッサでは、整数値0x12345678をポインタに変換すると、一般にアドレス0x12345678を参照するポインタが生成されます。ハードウェアリファレンスマニュアルには、その場所に関する特別なものがあるかどうかが示されます。もっと面白いアドレス空間を持つプロセッサでは、ハードウェアアドレス以外のものをポインタとして使う必要があります。たとえば、アンティークのIBM PCコンピュータでは、ディスプレイバッファはハードウェアアドレス0xB8000にマッピングされていましたが、ほとんどのコンパイラでは、アドレスは(short far *)0xB8000000と表示されます。

関連する問題