19

std::vector<unsigned char>インターネット上には、またはバイナリ データに類似したものを使用する必要があることを示唆する投稿がいくつかあります。

std::basic_stringしかし、多くの便利な文字列操作関数を提供するので、私はむしろそのバリアントを好みます。そして、私の知る限り、C++11以降、既知のすべてのC++03実装がすでに行っていること、つまりstd::basic_stringその内容をメモリに連続して保存することを標準が保証しています。

一見するstd::basic_string<unsigned char>と、良い選択かもしれません。

std::basic_string<unsigned char>ただし、ほとんどすべてのオペレーティング システム関数は のみを受け入れchar*、明示的なキャストが必要になるため、 は使用したくありません。また、文字列リテラルはconst char*であるため、文字列リテラルをバイナリ文字列に割り当てるたびに明示的なキャストが必要になりますがconst unsigned char*、これも避けたいと思います。また、ファイルまたはネットワーク バッファから読み書きするための関数も同様char*const char*ポインタを受け入れます。

これstd::stringは基本的に の typedef である を残しますstd::basic_string<char>

std::stringバイナリデータをstd::string使用することに関する唯一の潜在的な残りの問題 (私が見ることができる)は、 char(署名できる) を使用することです。

charsigned char、およびunsigned charは 3 つの異なるタイプでありchar、符号なしまたは符号付きのいずれかです。

したがって、 の実際のバイト値がchar11111111bから返されstd::string:operator[]、その値を確認したい場合、その値は255(char符号なしの場合) または「負の値」 (char数値表現に応じて符号付きの場合) のいずれかになります。 )。

11111111b同様に、実際のバイト値をに明示的に追加したい場合、が署名されていてtoの会話がオーバーフローになる場合std::string、単純に追加(char) (255)することは処理系で定義されている可能性があります (シグナルを生成することさえあります) 。charintchar

それで、これを回避する安全な方法はありstd::stringますか?

§3.10/15 は次のように述べています。

プログラムが、次の型以外のglvalueを介してオブジェクトの格納された値にアクセスしようとした場合、動作は未定義です。

  • [...]
  • オブジェクトの動的な型に対応する符号付きまたは符号なしの型である型、
  • [...]
  • char または unsigned char 型。

私がそれを正しく理解していれば、unsigned char*ポインターを使用して a の内容にアクセスして操作できるようにstd::stringなり、これも明確に定義されます。ビットパターンを として再解釈するだけunsigned charで、変更や情報の損失はありません。後者は、charsigned char、およびのすべてのビットをunsigned char値表現に使用する必要があるためです。

次に、このunsigned char*内容の解釈を、std::stringその範囲内のバイト値にアクセスして変更する手段として、それ自体[0, 255]の署名に関係なく、明確に定義された移植可能な方法で使用できcharます。

これにより、潜在的に署名された から生じる問題が解決されcharます。

私の仮定と結論は正しいですか?

また、unsigned char*同じビット パターン (11111111bまたは10101010b) の解釈は、すべての実装で同じであることが保証されていますか? 別の言い方をすれば、標準は「目を通して見る」ことを保証していunsigned charますか?同じビットパターンは常に同じ数値につながります(バイトのビット数が同じであると仮定します)?

したがって、C++ 11 でバイナリ データを格納および操作するために安全に (つまり、未定義または実装定義の動作なしで) 使用できますか?std::string

4

3 に答える 3

18

型 isの変換static_cast<char>(uc)は常に有効です。3.9.1 [basic.fundamental] によれば、 、 、およびの表現は、他の 2 つの型のいずれかと同一であることと同一です。ucunsigned charcharsigned charunsigned charchar

文字 (char) として宣言されたオブジェクトは、実装の基本文字セットのメンバーを格納するのに十分な大きさでなければなりません。このセットの文字が文字オブジェクトに格納されている場合、その文字オブジェクトの整数値は、その文字の単一文字リテラル形式の値と等しくなります。char オブジェクトが負の値を保持できるかどうかは実装定義です。文字は、署名なしまたは署名付きで明示的に宣言できます。Plain char、signed char、および unsigned char は 3 つの異なる型であり、まとめてナロー文字型と呼ばれます。char、signed char、および unsigned char は、同じ量のストレージを占有し、同じ配置要件 (3.11) を持ちます。つまり、それらは同じオブジェクト表現を持っています。ナロー文字型の場合、オブジェクト表現のすべてのビットが値表現に参加します。符号なしのナロー文字型の場合、値表現の可能なすべてのビット パターンは数値を表します。これらの要件は、他のタイプには当てはまりません。特定の実装では、プレーンな char オブジェクトは、signed char または unsigned char と同じ値を取ることができます。どちらが実装定義です。

もちろん、の範囲外の値を変換するunsigned charchar問題が発生し、未定義の動作が発生する可能性があります。つまり、おかしな値を に格納しようとしない限り、問題ありませんstd::string。ビットパターンに関しては、n2 に変換される th ビットに依存できますnstd::string慎重に処理すれば、バイナリ データを に格納するのに問題はないはずです。

とは言っても、私はあなたの前提には同意しません。バイナリ データの処理には、unsigned値を使用して操作するのが最適なバイトを処理する必要があります。誤って の使用を台無しにして、明示的に処理されない場合に変換して便利なエラーchar*を作成する必要があるいくつかのケースは、沈黙します! つまり、対処することでエラーを防ぐことができます。また、これらの優れた文字列関数をすべて取得できるという前提にも同意しません。1 つは、一般的にアルゴリズムを使用したほうがよいということですが、バイナリ データは文字列データではありません。要約すると、 の推奨事項は、何もないところから出てくるだけではありません! 見つけにくいトラップをデザインに組み込まないように意図的に設計されています。unsigned char*charunsigned charstd::vector<unsigned char>

使用を支持する唯一の適度に合理的な議論は、char文字列リテラルに関するものかもしれませんが、それでも C++11 に導入されたユーザー定義の文字列リテラルとは相容れません。

#include <cstddef>
unsigned char const* operator""_u (char const* s, size_t) 
{
    return reinterpret_cast<unsigned char const*>(s);
}

unsigned char const* hello = "hello"_u;
于 2013-11-03T21:01:07.193 に答える
1

はい、あなたの仮定は正しいです。バイナリ データを unsigned char のシーケンスとして std::string に格納します。

于 2013-11-03T20:46:16.877 に答える