web-dev-qa-db-ja.com

ポインターを整数に変換する

既存のコードを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ビットマシンで可能な限りきれいに動作するようにします。何か案が ?

75
PierreBdR

つかいます 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ファイルに入れて、必要な場所に含めてください。

または、Microsoftのstdint.hファイルを here から、またはポータブルなものを here から使用します。

65
Milan Babuškov

これが最新のC++の方法だと思います。

#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);

[〜#〜] edit [〜#〜]

整数の正しい型

ポインタを整数として保存する正しい方法は、uintptr_tまたはintptr_tタイプを使用することです。 (cppreference C99の整数型 も参照してください)。

これらの型は、C99の<stdint.h>および<cstdint>のC++ 11の名前空間stdで定義されています( 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は、この変換の正しいキャストです( here も参照)。

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

関連する質問

73
Alex

「size_t」と「ptrdiff_t」は、アーキテクチャに合わせて(それが何であれ)必要です。したがって、「int」を使用するのではなく、「size_t」を使用できるはずだと思います。これは、64ビットシステムでは64ビットタイプでなければなりません。

この議論 nsigned int vs size_t についてもう少し詳しく説明します。

41
Richard Corden

つかいます uintptr_t整数型として。

16
moonshadow

いくつかの答えは、「その」ソリューションとして_uintptr_t_および_#include <stdint.h>_を指摘しています。つまり、答えの一部ではなく、全体の答えではありません。また、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()内)に問題があることがわかります。キャストなしで整数をポインターに変換します。 function()をすべての使用法で分析して、値がどのように渡されるかを確認する必要があります。 function()内のコードは、呼び出しが記述されていれば機能します。

_unsigned long i = 0x2341;
function(1, &i);
_

あなたのものはおそらく異なって書かれているので、示されている値を使用することが理にかなっていることを確認するために、関数が呼び出されるポイントをレビューする必要があります。潜在的なバグを見つける可能性があることを忘れないでください。

また、(変換された)_void *_パラメーターの値をフォーマットする場合は、_<inttypes.h>_の代わりに_stdint.h_ヘッダーを注意深く見てください— _inttypes.h_はサービスを提供します_stdint.h_の例ですが、C99標準では[t] heeer _<inttypes.h>_はヘッダー_<stdint.h>_を含み、ホストされた実装によって提供される追加機能で拡張します =)書式文字列でPRIxxxマクロを使用します。

また、私のコメントはC++ではなくCに厳密に適用されますが、コードはCとC++の間で移植可能なC++のサブセットにあります。私のコメントが当てはまる可能性は十分にあります。

7
  1. #include <stdint.h>
  2. つかいます uintptr_t含まれている標準ヘッダーファイルで定義されている標準タイプ。
4
Michael S.

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.
*/

クレジットはコミッターにあります。

2
B.Mr.W.

この場合のvoid *の「意味」は一般的なハンドルだと思います。値へのポインタではなく、値そのものです。 (これは、CおよびC++プログラマーによるvoid *の使用方法にすぎません。)

整数値を保持している場合、整数の範囲内にある方が良いでしょう!

整数への簡単なレンダリングは次のとおりです。

int x = (char*)p - (char*)0;

警告を与えるだけです。

2
Craig Hicks

最善の方法は、ポインター型から非ポインター型への変換を避けることです。ただし、これは明らかにあなたのケースでは不可能です。

皆が言ったように、uintptr_tはあなたが使うべきものです。

この link には、64ビットコードへの変換に関する情報があります。

comp.std.c でこれについての良い議論もあります。

1
Benoit

uintptr_tC++/C++ 11に存在するとは限りません 、これが一方向の変換である場合、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");
0
Antonio