バイトのバッファは、signedcharまたはunsignedchar、あるいは単にcharバッファにする必要がありますか?CとC++の違いはありますか?
ありがとう。
任意のバイナリ データを保存する場合は、unsigned char
. これは、C 標準によってパディング ビットがないことが保証されている唯一のデータ型です。他の各データ型には、オブジェクト表現 (値を決定するビットだけではなく、オブジェクトのすべてのビットを含むもの) にパディング ビットが含まれる場合があります。パディング ビットの状態は指定されておらず、値の格納には使用されません。したがって、char
バイナリ データを使用して読み取ると、(値のビットのみを解釈することによって) char の値の範囲に切り詰められますが、無視されるだけで存在し、 によって読み取られるビットがまだ存在する可能性がありますmemcpy
。実際の構造体オブジェクトのパディング ビットによく似ています。タイプunsigned char
はそれらを含まないことが保証されています。それは5.2.4.2.1/2
(C99 TC2、ここではn1124)から続きます:
型 char のオブジェクトの値が式で使用されるときに符号付き整数として扱われる場合、 の値は の値と
CHAR_MIN
同じでSCHAR_MIN
あり、 の値は の値とCHAR_MAX
同じでなければなりませんSCHAR_MAX
。それ以外の場合、 の値はCHAR_MIN
0 であり、 の値は の値とCHAR_MAX
同じですUCHAR_MAX
。値UCHAR_MAX
は等しくなければならない2^CHAR_BIT − 1
最後の文から、パディング ビット用のスペースが残っていないことがわかります。バッファの型としてを使用する場合char
、オーバーフローの問題もあります。8
ビットの範囲内にあるそのような要素に任意の値を明示的に割り当てます。 a char
、これはCHAR_MIN
..CHAR_MAX
であり、このような変換はオーバーフローし、シグナルの発生を含む実装定義の結果を引き起こします。
上記に関する問題が実際の実装ではおそらく表示されない場合でも (実装の品質が非常に低くなる可能性があります)、最初から適切な型を使用することをお勧めしますunsigned char
。
ただし、文字列の場合、選択するデータ型は ですchar
。これは、文字列関数と印刷関数によって理解されます。signed char
これらの目的で使用することは、私にとって間違った決定のように思えます。
詳細についてthis proposal
は、C 標準の次のバージョンの修正が含まれているものをお読みください。最終的にはsigned char
パディング ビットも不要になります。すでにワーキングペーパーに組み込まれています。
バイトのバッファは、signed char か unsigned char か、単に char バッファか? C と C++ の違いはありますか?
言語がそれを処理する方法の小さな違い。慣習がそれをどのように扱うかの大きな違い。
char
= ASCII (または UTF-8、ただし符号が邪魔になる)テキストデータunsigned char
= バイトsigned char
= めったに使用されないそして、そのような区別に依存するコードがあります。ちょうど 1 週間か 2 週間前に、JPEG データが Base64 エンコード関数のバージョンに渡されたために破損するというバグに遭遇しました。これchar*
は、「文字列」内の無効な UTF-8 をすべて「うまく」置き換えました。BYTE
別名に変更するunsigned char
だけで修正できました。
場合によります。
char
バッファがテキストを保持することを意図している場合、それを の配列として宣言し、それがデフォルトで署名されているか署名されていないかをプラットフォームに判断させることがおそらく理にかなっています。これにより、たとえば、実装のランタイム ライブラリとの間でデータをやり取りする際のトラブルを最小限に抑えることができます。
バッファーがバイナリ データを保持することを意図している場合は、使用方法によって異なります。たとえば、バイナリ データが実際には、符号付きの 8 ビット固定小数点 ADC 測定値であるデータ サンプルのパックされた配列である場合signed char
が最適です。
ほとんどの実際のケースでは、バッファは単なるバッファであり、個々のバイトのタイプはあまり気にしません。これは、一括操作でバッファをいっぱいにして、それをパーサーを使用して複雑なデータ構造を解釈し、何か役に立つことを行います。その場合は、最も簡単な方法で宣言してください。
マシンのデフォルトロケールの文字列ではなく、実際には8ビットバイトのバッファである場合は、を使用しますuint8_t
。charがバイト(またはバイトがオクテット)ではないマシンがたくさんあるわけではありませんが、「これは文字列です」ではなく「これはオクテットのバッファです」というステートメントを作成すると、多くの場合、役立つドキュメントになります。
charまたはunsigned charのいずれかを使用する必要がありますが、signed charは使用しないでください。規格は 3.9/2 で次のとおりです。
POD 型 T のオブジェクト (基本クラスのサブオブジェクト以外) の場合、オブジェクトが型 T の有効な値を保持しているかどうかにかかわらず、オブジェクトを構成する基になるバイト (1.7) を char または unsigned の配列にコピーできます。 char.char または unsigned char の配列の内容がオブジェクトにコピーされた場合、オブジェクトはその後元の値を保持します。
最大限の移植性を得るには、常にunsignedcharを使用してください。これが関係する可能性のある例がいくつかあります。異なるエンディアンタイプのシステム間で共有されるシリアル化されたデータがすぐに思い浮かびます。シフトまたはビットマスキングを実行する場合、値は別です。
unsigned char として定義した方がよいでしょう。実際、Win32 型の BYTE は unsigned char として定義されています。この間に C と C++ の違いはありません。
int8_t と uint8_t の選択は、ptr を NULL と比較する場合と似ています。
機能の観点からは、NULL との比較は 0 との比較と同じです。NULL は 0 の #define であるためです。
しかし、個人的には、コーディングスタイルの観点から、私は自分のポインターを NULL と比較することを選択します。なぜなら、NULL #define は、コードを保守している人に、あなたが悪いポインターをチェックしていることを暗示しているからです...
VS
誰かが 0 との比較を見た場合、それはあなたが特定の値をチェックしていることを意味します。
上記の理由から、uint8_t を使用します。
要素をより広い変数にフェッチすると、もちろん符号拡張されるかどうかになります。
データのバイナリ性を強調したい場合は、より「生」のように感じられ、「ねえ、それはただの小さな集まりだ」と言うのはあまり魅力的ではないので、私は unsigned を好む傾向があります。ints
signed char
バイトのバッファを表すために明示的に使用したことはないと思います。
もちろん、3 番目の選択肢の 1 つは、バッファーをできるだけ表現するvoid *
ことです。多くの一般的な I/O 関数は で動作するvoid *
ため、使用する整数型の決定を完全にカプセル化できる場合があります。これは便利です。
数年前、128 を超える ASCII 値に対して色付きの文字を出力する C++ コンソール アプリケーションで問題が発生しました。これは、char から unsigned char に切り替えることで解決されましたが、char 型を維持しながらも解決できたと思います。
今のところ、ほとんどの C/C++ 関数は char を使用しており、両方の言語をよりよく理解できるようになったので、ほとんどの場合に char を使用しています。
本当に気にする?そうでない場合は、デフォルト (char) をそのまま使用し、重要でない事項でコードを乱雑にしないでください。そうしないと、将来のメンテナーは、なぜ署名付き (または署名なし) を使用したのか不思議に思うでしょう。彼らの生活をよりシンプルにします。
typedef char byte;
byte
これで、配列をsにすることができます。意図したことは誰の目にも明らかであり、機能が失われることはありません。
少しばかげていることはわかっていますが、意図したとおりにコードを 100% 読み取ることができます。
コンパイラに嘘をつくと、罰せられます。
通過するだけのデータがバッファに含まれていて、それらを操作しない場合は問題ありません。
ただし、バッファの内容を操作する必要がある場合は、正しい型宣言によってコードが単純になります。「int val = buf[i] & 0xff;」なし ナンセンス。
そのため、データが実際に何であり、それをどのように使用する必要があるかを考えてください。