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


85

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


5
これが古い投稿を掘り起こしていることは知っていますが、受け入れられた答えは完全に正しくないようです。size_t動作しない具体的な例は、i386セグメントメモリです。32ビットマシンが、sizeof返す2ためにsize_t以下のアレックスの答えは正しいようです。アレックスの答えは、uintptr_tほぼすべての場所で機能し、現在は標準となっています。これはC ++ 11処理を提供し、C ++ 03ヘッダーガードも提供します。
jww 2016

回答:


68

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

または、ここstdint.hからMicrosoftバージョンのファイルをダウンロードするか、ここからポータブルファイルを使用することもできます。


MSVC(および場合によってはBorland)で動作するstdint.hを取得する方法については、stackoverflow.com / questions / 126279 /…を参照してください。
Michael Burr

2
両方のリンクが壊れています!
アントニオ

1
この回答はCに関連していますが、言語にはC ++のタグが付けられているため、私が探していた回答ではありません。
haseeB Mir 2018

@HaSeeBMiR適切な修正は、に切り替える<cstdint>か、をダウンロードしたcstdint場合は適切なものをダウンロードすることstdint.hです。
ジャスティンタイム-モニカを復活

1
@HaSeeBMiR答えがC ++ではなくCに関連している唯一の理由は、同等のC ++ヘッダーではなくCヘッダーを使用しているためです。CプリプロセッサはC ++cstdintの一部であり、そこで定義されているすべての型名と同様に、C ++標準の一部です。それは確かに指定されたタグに適しています。...型を手動で定義することに同意しませんが、そうしないコンパイラを使用する場合は必要になる場合があります。
ジャスティンタイム-モニカを復活させる

91

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

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

編集

整数の正しい型

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

これらの型は<stdint.h>、C99の場合とstdC ++ 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

関連する質問



<cstdint>を含める場合は、代わりにstd :: uintptr_tも使用することをお勧めします。
linleno 2015年

すごい...キャストは私が探していたものです。のuintptr_t代わりに使用するように指示された場合size_t、なぜそれが必要なのreinterpret_castですか?static_cast標準では互換性のあるデータ型が具体的に提供されているため、簡単に行う必要があるようです...
jww 2016

1
@jww read:en.cppreference.com/w/cpp/language/static_castここでの私の理解はstatic_cast、型を変換する可能性があるか、それがポインターである場合は、型が必要とする場合にポインター調整を行う可能性があるということです。reinterpret_cast実際には、基礎となるメモリパターンのタイプを変更するだけです(突然変異はありません)。明確にするために:static_castここでは同じように動作します。
アレクサンダーオハイオ州

2
これは、CおよびC ++でキャストする方法のすべての詳細を提供するため、代わりに選択された回答としてマークする必要があります
haseeB Mir 2018

42

'size_t'と 'ptrdiff_t'は、アーキテクチャー(それが何であれ)と一致する必要があります。したがって、「int」を使用するのではなく、「size_t」を使用できるはずです。これは、64ビットシステムでは64ビット型である必要があります。

この議論unsignedintとsize_tについては、もう少し詳しく説明します。


34
size_tは通常、ポインタを保持するのに十分な大きさですが、必ずしもそうであるとは限りません。stdint.hヘッダーを見つけて(コンパイラーにまだヘッダーがない場合)、uintptr_tを使用することをお勧めします。
Michael Burr

3
残念ながら、唯一の制約size_tは、任意のの結果を保持する必要があるということですsizeof()。これは必ずしもx64で64ビットになるとは限りません。参照
アントワーヌ

3
size_t 非メンバーポインタの値を安全に格納できます。en.cppreference.com/w/cpp/types/size_tを参照してください。
AndyJost 2016

2
@AndyJostいいえ、できません。あなた自身のリンクでさえそれを確認します。
yyny 2016

1
@YoYoYonnY:「多くのプラットフォーム(例外はセグメント化されたアドレス指定のシステム)では、std :: size_tは非メンバーポインタの値を安全に格納できます。この場合、std :: uintptr_tと同義です。」-何を言ってるの?
slashmais 2018年


8

いくつかの答えが指摘し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
$

呼び出し場所(in main())に問題があることに気付くでしょう—キャストなしで整数をポインターに変換します。function()値がどのように渡されるかを確認するには、すべての使用法を分析する必要があります。私の中のコードfunction()は、呼び出しが書かれていれば機能します:

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

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

また、次の値のフォーマットしようとしている場合void *では慎重に見て、(変換後のような)パラメータを<inttypes.h>ヘッダ(代わりにstdint.h-inttypes.hのサービスを提供していstdint.h異例である、しかし、C99の標準が言う彼は、ヘッダは[T]<inttypes.h>ヘッダを含みます<stdint.h>とホストされた実装によって提供される追加機能で拡張し、フォーマット文字列でPRIxxxマクロを使用します。

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


3
あなたは私の質問の要点を見逃したと思います。コードは整数の値をポインターに格納しています。そして、コードのその部分は反対のことをしています(たとえば、ポインターとして書き込また整数の値を抽出する)。
PierreBdR 2014年

@PierreBdRそれにもかかわらず、彼は非常に有効な主張をしています。符号付き整数を使用するがサイズに使用され、符号なしに変更しても問題ないと考えるコード(コンパイラーが警告する場合を含む)を見るほど単純ではありません。残念ながら、それは必ずしもそれほど単純ではありません。潜在的なバグを引き起こしたい場合を除いて、それぞれのケースを明示的に調べる必要があります-そしてそれで微妙なバグがあります。
プリフタン

4
  1. #include <stdint.h>
  2. uintptr_tインクルードされた標準ヘッダーファイルで定義されている標準タイプを使用します。

3

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

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

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

このリンクには、64ビットコードへの変換に関する優れた情報があります。

comp.std.cにもこれに関する良い議論があります


2

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

整数値を保持している場合は、整数範囲内にある方がよいでしょう。

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

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

警告を出すだけです。


0

以来をuintptr_tされてC ++ / 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");

uintptr_tがない場合、uintmax_tも答えではありません。ポインターの値を格納できる保証はありません。それを行う整数型はないかもしれません。
PierreBdR
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.