26

私は C++ を扱うのは初めてで、この言語の複雑さと機微をすべて把握していません。

C++ 11で任意の型のポインタに任意のバイトオフセットを追加する最も移植性が高く、正しく安全な方法は何ですか?

SomeType* ptr;
int offset = 12345 /* bytes */;
ptr = ptr + offset;             // <--

Stack Overflow と Google で多くの回答を見つけましたが、それらはすべて異なることを提案しています。私が遭遇したいくつかの変種:

  1. にキャストchar *:

    ptr = (SomeType*)(((char*)ptr) + offset);
    
  2. にキャストunsigned int:

    ptr = (SomeType*)((unsigned int)ptr) + offset);
    
  3. にキャストsize_t:

    ptr = (SomeType*)((size_t)ptr) + offset);
    
  4. 「 と のサイズは常にポインターのサイズsize_tptrdiff_t一致します。このため、これらの型は、大きな配列のインデックス、ポインターの格納、およびポインター演算に使用する必要があります。」- CodeProject のsize_t と ptrdiff_tについて

    ptr = (SomeType*)((size_t)ptr + (ptrdiff_t)offset);
    
  5. または、前のように、intptr_t代わりに を使用size_tします。これは、署名されていないのではなく署名されています。

    ptr = (SomeType*)((intptr_t)ptr + (ptrdiff_t)offset);
    
  6. はすでに符号付き整数であり、 ではないintptr_tため、 にのみキャストします。offsetintptr_tsize_t

    ptr = (SomeType*)((intptr_t)ptr) + offset);
    

そして、これらすべての場合において、古い C スタイルのキャストを使用することは安全ですか、それとも使用するのがより安全で移植性が高いstatic_castreinterpret_castでしょうか?

ポインター値自体が署名されていないか署名されていると想定する必要がありますか?

4

5 に答える 5

12

私は次のようなものを使用します:

unsigned char* bytePtr = reinterpret_cast<unsigned char*>(ptr);
bytePtr += offset;
于 2013-04-10T18:58:16.437 に答える
12

使用reinterpret_cast(または C スタイルのキャスト) は、型システムを回避することを意味し、移植性がなく、安全ではありません。それが正しいかどうかは、アーキテクチャによって異なります。あなたがそれをしなければならない場合、あなたは自分が何をしているのかを知っているとほのめかし、それ以降は基本的に独りでいます. 警告は以上です。

nポインタまたは型に数値を追加すると、このポインタは型の要素Tごとに移動します。あなたが探しているのは、1要素が1バイトを意味するタイプです。n T

sizeofセクション 5.3.3.1から:

sizeof 演算子は、そのオペランドのオブジェクト表現のバイト数を返します。[...] sizeof(char)sizeof(signed char)および1sizeof(unsigned char)です。他の基本型 (3.9.1) に適用される sizeof の結果は実装定義です。

sizeof(int)などについての記述はありませんのでご注意ください。

バイトの定義(セクション 1.7.1.):

C++ メモリ モデルの基本的な記憶単位はバイトです。1 バイトは少なくとも、基本実行文字セット (2.3) の任意のメンバーと、Unicode UTF-8 エンコード形式の 8 ビット コード単位を格納するのに十分な大きさであり、連続するビット シーケンスで構成されています。その数は次のとおりです。実装定義。[...] C++ プログラムで使用できるメモリは、1 つまたは複数の連続したバイト シーケンスで構成されます。すべてのバイトには固有のアドレスがあります。

したがって、sizeofバイト数を返し、sizeof(char)1 の場合、charC++ には 1 バイトのサイズがあります。したがって、論理的charにはC++ のバイトですが、必ずしも事実上の標準の 8 ビット バイトではありません。a に追加すると、(C++ メモリ モデルに関して) バイト先のポインターが返されます。したがって、オブジェクトのポインターをバイトごとに操作する危険なゲームをプレイしたい場合は、それをバリアントの 1 つにキャストする必要があります。型に のような修飾子もある場合は、それらを「バイト型」にも転送する必要があります。nchar*ncharconst

    template <typename Dst, typename Src>
    struct adopt_const {
        using type = typename std::conditional< std::is_const<Src>::value,
            typename std::add_const<Dst>::type, Dst>::type;
    };

    template <typename Dst, typename Src>
    struct adopt_volatile {
        using type = typename std::conditional< std::is_volatile<Src>::value,
            typename std::add_volatile<Dst>::type, Dst>::type;
    };

    template <typename Dst, typename Src>
    struct adopt_cv {
        using type = typename adopt_const<
            typename adopt_volatile<Dst, Src>::type, Src>::type;
    };

    template <typename T>
    T*  add_offset(T* p, std::ptrdiff_t delta) noexcept {
        using byte_type = typename adopt_cv<unsigned char, T>::type;
        return reinterpret_cast<T*>(reinterpret_cast<byte_type*>(p) + delta);
    }

于 2013-04-11T08:13:08.630 に答える
-2

あなたが持っている場合:

myType *ptr;

あなたがやる:

ptr+=3;

コンパイラは、次の方法で変数を確実にインクリメントします。

3*sizeof(myType)

そして、それは私が知る限り、それを行う標準的な方法です。

反復したい場合は、タイプ myType の要素の配列を言ってみましょう。それがそれを行う方法です。

わかりました、キャストしたい場合は、

myNewType *newPtr=reinterpret_cast < myNewType * > ( ptr )

または、単純な古い C に固執して、次のようにします。

myNewType *newPtr=(myNewType *) ptr;

そしてインクリメント

于 2013-04-10T19:01:37.653 に答える