質問
既存のコードを64ビットマシンに適合させようとしています。主な問題は、1つの関数で、以前のコーダーが関数自体で適切な型に変換されるvoid *引数を使用することです。短い例:
void function(MESSAGE_ID id, void* param)
{
if(id == FOO) {
int real_param = (int)param;
// ...
}
}
もちろん、64ビットマシンでは、エラーが発生します:
error: cast from 'void*' to 'int' loses precision
これを修正して、32ビットマシンで可能な限りきれいに動作するようにします。アイデアはありますか?
解決
intptr_t
およびuintptr_t
を使用します。
移植可能な方法で定義されるようにするには、次のようなコードを使用できます。
#if defined(__BORLANDC__)
typedef unsigned char uint8_t;
typedef __int64 int64_t;
typedef unsigned long uintptr_t;
#elif defined(_MSC_VER)
typedef unsigned char uint8_t;
typedef __int64 int64_t;
#else
#include <stdint.h>
#endif
それをいくつかの.hファイルに配置し、必要な場所に含めます。
他のヒント
これが最新のC ++の方法だと思います。
#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);
編集:
整数の正しい型
したがって、ポインタを整数として保存する正しい方法は、uintptr_t
またはintptr_t
タイプを使用することです。 (cppreference C99の整数型も参照してください。)
これらのタイプは、C99の<stdint.h>
およびstd
のC ++ 11のネームスペース<cstdint>
で定義されています( C ++の整数型)。
C ++ 11(およびそれ以降)バージョン
#include <cstdint>
std::uintptr_t i;
C ++ 03バージョン
extern "C" {
#include <stdint.h>
}
uintptr_t i;
C99バージョン
#include <stdint.h>
uintptr_t i;
正しいキャスト演算子
Cにはキャストが1つしかないため、C ++でCキャストを使用することは好ましくありません(C ++では使用しないでください)。 C ++には、さまざまなキャストがあります。 reinterpret_cast
は、この変換の正しいキャストです(こちらも参照)。
C ++ 11バージョン
auto i = reinterpret_cast<std::uintptr_t>(p);
C ++ 03バージョン
uintptr_t i = reinterpret_cast<uintptr_t>(p);
Cバージョン
uintptr_t i = (uintptr_t)p; // C Version
関連する質問
'size_t'と 'ptrdiff_t'は、アーキテクチャに合わせて(それが何であれ)必要です。したがって、「int」を使用するのではなく、「size_t」を使用できるはずだと思います。「size_t」は、64ビットシステムでは64ビットタイプでなければなりません。
この議論 unsigned int vs size_t はもう少し詳しく説明します。
整数型としてuintptr_t
を使用します。
いくつかの回答は、uintptr_t
および#include <stdint.h>
を「the」ソリューションとして示しています。つまり、答えの一部ではありますが、全体の答えではありません。 FOOのメッセージIDで関数が呼び出される場所も確認する必要があります。
このコードとコンパイルを検討してください:
$ cat kk.c
#include <stdio.h>
static void function(int n, void *p)
{
unsigned long z = *(unsigned long *)p;
printf("%d - %lu\n", n, z);
}
int main(void)
{
function(1, 2);
return(0);
}
$ rmk kk
gcc -m64 -g -O -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith \
-Wcast-qual -Wstrict-prototypes -Wmissing-prototypes \
-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE kk.c -o kk
kk.c: In function 'main':
kk.c:10: warning: passing argument 2 of 'func' makes pointer from integer without a cast
$
呼び出し場所(main()
)に問題があることがわかります<!>#8212;キャストなしで整数をポインターに変換します。値がどのように渡されるかを確認するために、すべての使用法でfunction()
を分析する必要があります。呼び出しが記述されていれば、私のvoid *
内のコードは機能します。
unsigned long i = 0x2341;
function(1, &i);
あなたのものはおそらく異なって書かれているので、示されている値を使用することが理にかなっていることを確認するために、関数が呼び出されるポイントをレビューする必要があります。潜在的なバグを見つけている可能性があることを忘れないでください。
また、(変換された)<inttypes.h>
パラメーターの値をフォーマットする場合は、(stdint.h
<!>#8212の代わりにinttypes.h
ヘッダーを注意深く見てください; <stdint.h>
はサービスを提供します<=>の、これは珍しいことですが、C99標準では、ヘッダー<=>にはヘッダー<=>が含まれており、
ホストされた実装によって提供される追加機能)およびフォーマット文字列でPRIxxxマクロを使用します。
また、私のコメントはC ++ではなくCに厳密に適用できますが、コードはC ++とC ++の間で移植可能なC ++のサブセットにあります。私のコメントが当てはまる可能性は十分にあります。
-
#include <stdint.h>
- 含まれている標準ヘッダーファイルで定義されている
uintptr_t
標準タイプを使用します。
<!> quot; meaning <!> quot;この場合のvoid *は汎用ハンドルです。 値へのポインタではなく、値そのものです。 (これは、CおよびC ++プログラマーがvoid *を使用する方法です。)
整数値を保持している場合は、整数範囲内にある方が良いでしょう!
整数への簡単なレンダリングを次に示します。
int x = (char*)p - (char*)0;
警告のみを表示する必要があります。
SQLite のソースコードを調べているときに、この質問に出会いました。
sqliteInt.h には、整数とポインター間のマクロ変換を定義したコードの段落です。著者は、まずコンパイラに依存する問題であるべきだと指摘した非常に良い声明を出してから、そのソリューションを実装して、一般的なコンパイラのほとんどを説明しました。
#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
#else /* Generates a warning - but it always works */
# define SQLITE_INT_TO_PTR(X) ((void*)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(X))
#endif
詳細については、コメントの引用をご覧ください:
/*
** The following macros are used to cast pointers to integers and
** integers to pointers. The way you do this varies from one compiler
** to the next, so we have developed the following set of #if statements
** to generate appropriate macros for a wide range of compilers.
**
** The correct "ANSI" way to do this is to use the intptr_t type.
** Unfortunately, that typedef is not available on all compilers, or
** if it is available, it requires an #include of specific headers
** that vary from one machine to the next.
**
** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
** So we have to define the macros in different ways depending on the
** compiler.
*/
クレジットはコミッターに送られます。
最善の方法は、ポインター型から非ポインター型への変換を避けることです。 ただし、これは明らかにあなたのケースでは不可能です。
誰もが言ったように、uintptr_tはあなたが使うべきものです。
このリンクには良い情報があります64ビットコードへの変換について。
uintptr_t
は C ++ / C ++ 11に存在することを保証されていないため、これが1つである場合常に uintmax_t
で定義されている<cstdint>
を考慮することができる方法変換。
auto real_param = reinterpret_cast<uintmax_t>(param);
安全にプレイするために、コードの任意の場所にアサーションを追加できます:
static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
"No suitable integer type for conversion from pointer type");