メモリの観点から整数値をvoid*
またはその逆に変換するとはどういう意味ですか?私の理解はvoid*
は不特定の長さのメモリブロックへのアドレスです。
これは、Appleをオレンジと比較するようなものです。
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の本からの抜粋です。
int
からvoid *
へのキャストは意味がなく、非ポインターをポインターにキャストしようとしているため、実行しないでください。 C99標準 を引用すると、セクション6.3.2.3の項目5:
整数は任意のポインタ型に変換できます。以前に指定された場合を除き、結果は実装定義であり、正しく整列されていない可能性があり、参照されたタイプのエンティティをポイントしていない可能性があり、トラップ表現である可能性があります。
あなたがint *
をvoid *
にキャストできます(どのポインタもvoid *
に変換可能であり、すべてのポインターの「基本タイプ」)。
void *
からint
へのキャストは移植性がなく、使用するプラットフォームによっては完全に間違っている可能性があります(例:void *
多分64ビット幅、int
は32ビットのみ)。 C99標準を再度引用すると、セクション6.3.2.3項目6:
ポインタ型は整数型に変換できます。前述の場合を除き、結果は実装定義です。結果を整数型で表現できない場合の動作は未定義です。結果は、整数型の値の範囲内である必要はありません。
これを解決するために、一部のプラットフォームでは、ポインターを適切な幅の数値として扱うことができるuintptr_t
を提供しています。
C規格では、voidポインターを整数型に変換して、整数型をvoidポインターに戻すと同じポインターが得られるようにする必要があると規定されています。 nullポインターを整数に変換して値0を生成する必要があるかどうか、または数値リテラル0のみが特殊として認識されるかどうかはわかりません。多くの実装では、整数値はハードウェアアドレスを表しますが、標準ではそのような保証はありません。ハードウェアアドレス0x12345678の特別なトラップを含むハードウェアでは、ポインターを整数に変換するとハードウェアアドレスから0x12345678が減算され、整数をポインターに変換すると0x12345678が追加されます(したがって、整数値ゼロはnullポインターを表します)。
多くの場合、特に組み込みコントローラー用に開発する場合、コンパイラベンダーは、特定の整数値をポインター型に変換するときにアクセスするハードウェアアドレスを明示的に指定します。単一の線形アドレス空間を持つプロセッサでは、整数値0x12345678をポインタに変換すると、通常、アドレス0x12345678を参照するポインタが生成されます。ハードウェアリファレンスマニュアルには、その場所について何か特別なものがあったかどうかが示されます。より「興味深い」アドレス空間を持つプロセッサでは、ハードウェアアドレス以外のものをポインタとして使用する必要がある場合があります。たとえば、旧式のIBM PCコンピューターでは、ディスプレイバッファーはハードウェアアドレス0xB8000にマップされていましたが、ほとんどのコンパイラーでは、アドレスは(short far *)0xB8000000として表されます。
両方とも void*
ポインタ(またはそのことについては任意のポインタ)とint
は、大まかに言えば数値です。それらは異なるビットサイズである可能性がありますが、ポインタがint
よりも小さい可能性は低いため、操作を元に戻すことができます。もちろん、これは違法であり、有効な位置を示さないポインタを逆参照してはなりません。
これはリンゴとオレンジを比較するのとよく似ています。このコードが機能する唯一の方法は、明示的に前後にキャストしているためです。
Cコンパイラは、intにcharを保持するのと同じように、バイトをintからポインタ用のスペースにコピーして戻します。
これは、何らかの理由でプラットフォームの「int」が「void *」よりも長い場合、数値がめちゃくちゃになることさえあります。
実際に整数からポインタに変換して戻す数値が必要な場合は、intptr_t
を調べてください。その型は、実際には整数とポインタの両方を格納するように設計されています。
ポインタ演算のためにポインタを整数型にキャストできると便利です。たとえば、構造体内のメンバーのオフセットを計算する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);
//...
}
これは、この種のマクロとポインタの計算なしでは実行できません。しかし、それはツールチェーンに非常に依存しています。
比較しようとすると、リンゴとオレンジを比較するようなものです。しかし、ありません。基本的に、世のほとんどのアーキテクチャでは、情報を失うことなくintをvoidポインターにキャストできます。また、情報を失うことなく、voidポインターをintにキャストできます。もちろん、意味のあるメモリの場所を指さないため、そのポインタの逆参照を試みてはなりません。これは、キャスト前に整数が使用していたビットパターンと同じビットパターンを含む変数にすぎません。