1

いくつかのアイテムのバイナリシリアル化を実行していて、それらを不透明なバイトバッファに書き込んでいる状況があります:

int SerializeToBuffer(unsigned char* buffer)
{
    stringstream ss;
    vector<Serializable> items = GetSerializables();
    string serializedItem("");
    short len = 0;
    for(int i = 0; i < items.size(); ++i)
    {
        serializedItem = items[i].Serialize();
        len = serializedItem.length();

        // Write the bytes to the stream
        ss.write(*(char*)&(len), 2);
        ss.write(serializedItem.c_str(), len);

    }
    buffer = reinterpret_cast<unsigned char*>(
                const_cast<char*>(ss.str().c_str()));
    return items.size();
}

constから -nessを削除してから結果をバッファに割り当てても安全ss.str().c_str()ですreinterpret_castunsigned char*?

注: コードは、私が何をしているかを示すためのものであり、必ずしもコンパイルされるとは限りません。

4

5 に答える 5

3

本質的に定数の文字列の const 性を削除しても、Undefined Behaviorは発生しません。

const char* c_str ( ) const;
同等の C 文字列を取得する

文字列オブジェクトと同じ内容で null で終わる一連の文字 (c-string) を生成し、それを文字配列へのポインターとして返します。
終了ヌル文字が自動的に追加されます。
返された配列は、この一連の文字に必要な記憶領域とその終端のヌル文字を含む内部の場所を指しますが、この配列の値はプログラムで変更してはならず、次の呼び出しまで変更されないことが保証されているだけです。文字列オブジェクトの非定数メンバー関数。

于 2011-08-11T19:19:29.897 に答える
1

この関数の主な利用者は C# アプリケーションであるように思われるため、署名をより C# に適したものにすることから始めることをお勧めします。私が本当に時間に追われていて、「正しい方法」をする時間がなかった場合に私がすることは次のとおりです;-]

using System::Runtime::InteropServices::OutAttribute;

void SerializeToBuffer([Out] array<unsigned char>^% buffer)
{
    using System::Runtime::InteropServices::Marshal;

    vector<Serializable> const& items = GetSerializables();
    // or, if Serializable::Serialize() is non-const (which it shouldn't be)
    //vector<Serializable> items = GetSerializables();

    ostringstream ss(ios_base::binary);
    for (size_t i = 0u; i != items.size(); ++i)
    {
        string const& serializedItem = items[i].Serialize();
        unsigned short const len =
            static_cast<unsigned short>(serializedItem.size());

        ss.write(reinterpret_cast<char const*>(&len), sizeof(unsigned short));
        ss.write(serializedItem.data(), len);
    }

    string const& s = ss.str();
    buffer = gcnew array<unsigned char>(static_cast<int>(s.size()));
    Marshal::Copy(
        IntPtr(const_cast<char*>(s.data())),
        buffer,
        0,
        buffer->Length
    );
}

C# コードでは、これには署名があります。

void SerializeToBuffer(out byte[] buffer);
于 2011-08-11T20:28:29.533 に答える
1

短い答え: いいえ

長い答え: いいえ。実際にはできません。それらのオブジェクトの内部バッファはオブジェクトに属します。内部構造への参照を取ることは間違いなく禁止であり、カプセル化を破ります。とにかく、これらのオブジェクト (内部バッファーを含む) は関数の最後に破棄され、buffer変数は初期化されていないメモリを指します。

の使用const_cast<>は、通常、設計に何か問題があることを示しています。
の使用は、reinterpret_cast<>通常、間違っていることを意味します (または、非常に低レベルのことを行っています)。

おそらく次のように書きたいと思うでしょう:

std::ostream& operator<<(std::ostream& stream, Data const& serializable)
{
    return stream << serializable.internalData;

    // Or if you want to write binary data to the file:

    stream.write(static_cast<char*>(&serializable.internalData), sizeof(serializable.internalData);
    return stream;

}
于 2011-08-11T19:25:30.407 に答える
1

これは安全ではありません。部分的には を取り除いているためですconstが、さらに重要なのは、関数が戻るときに再利用される配列へのポインターを返しているためです。

あなたが書くとき

ss.str().c_str()

の戻り値は、それを呼び出しc_str()たオブジェクトがまだ存在する限り有効です。stringの署名stringstream::str()

string stringstream::str() const;

つまり、一時stringオブジェクトを返します。その結果、すぐにライン

ss.str().c_str()

実行が終了すると、一時stringオブジェクトが回収されます。これは、経由で受け取った未処理のポインターがc_str()無効になり、それを使用すると未定義の動作が発生することを意味します。

これを修正するには、本当に を返すunsigned char*必要がある場合は、C スタイルの文字列を手動で独自のバッファーにコピーする必要があります。

/* Get a copy of the string that won't be automatically destroyed at the end of a statement. */
string value = ss.str();

/* Extract the C-style string. */
const char* cStr = value.c_str();

/* Allocate a buffer and copy the contents of cStr into it. */
unsigned char* result = new unsigned char[value.length() + 1];
copy(cStr, cStr + value.length() + 1, result);

/* Hand back the result. */
return result;

さらに、@Als が指摘したconstように、コンテンツの変更を計画している場合、の削除は悪い考えです。内容を変更していない場合は問題ありませんが、 のconst unsigned char*代わりに を返す必要がありunsigned char*ます。

お役に立てれば!

于 2011-08-11T19:25:51.967 に答える
0

根本的な問題は次のとおりです。

buffer = ... ;
return items.size();

最後から 2 番目の行では、関数が引数として与えられたポインターを保持するために (その時点まで) 使用されていたローカル変数に新しい値を割り当てています。次に、その直後に関数から戻り、割り当てたばかりの変数に関するすべてを忘れます。それは意味がありません!

おそらくやりたいことは、 が指すss_str().c_str()メモリから に格納さたポインタが指すメモリにデータをコピーすることbufferです。何かのようなもの

memcpy(buffer, ss_str().s_str(), <an appropriate length here>)
于 2011-08-11T19:26:21.587 に答える