バイトのバッファは、signed char、unsigned char、または単にcharバッファにする必要がありますか? CとC++の違いは何ですか?
ありがとう。
バイトのバッファは、signed char、unsigned char、または単にcharバッファにする必要がありますか? CとC++の違いは何ですか?
言語による処理方法の小さな違い。 A huge慣習による扱いの違い。
char
= ASCII(またはUTF-8ですが、署名がそこに含まれています)textualデータunsigned char
=バイトsigned char
=めったに使用されないそして、そのような区別にが依存するコードがあります。ほんの1〜2週間前に、Base64エンコード関数のchar*
バージョンに渡されたためにJPEGデータが破損するというバグに遭遇しました。 」 BYTE
aka unsigned char
に変更するだけで、問題を修正できました。
任意のバイナリデータを格納する場合は、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
ビットの範囲にあるそのような要素の1つに値を明示的に割り当てると、そのようなことが予想されます。割り当てはOKですが、CHAR_MIN
..CHAR_MAX
であるchar
の範囲内ではないため、このような変換はオーバーフローし、シグナルの生成など、実装で定義された結果を引き起こします。
上記に関する問題が実際の実装ではおそらく表示されない場合でも(very実装の質が低い場合)、正しい方法を使用することをお勧めしますunsigned char
という最初から入力します。
ただし、文字列の場合、選択できるデータ型はchar
です。これは、文字列関数と出力関数で認識されます。これらの目的でsigned char
を使用することは、私には間違った判断のように見えます。
詳細については、 this proposal
をお読みください。これには、C標準の次のバージョンの修正が含まれ、最終的にはsigned char
にパディングビットが不要になります。 ワーキングペーパー にはすでに組み込まれています。
場合によります。
バッファーがテキストを保持することを目的としている場合、char
の配列としてそれを宣言し、デフォルトで署名するか署名しないかをプラットフォームに決定させることはおそらく意味があります。これにより、たとえば、実装のランタイムライブラリとの間でデータをやり取りする際の問題が最小限になります。
バッファーがバイナリー・データを保持することを意図している場合は、それをどのように使用するかによって異なります。たとえば、バイナリデータが実際に符号付き8ビット固定小数点ADC測定値であるデータサンプルのパックされた配列である場合、signed char
が最適です。
ほとんどの実際のケースでは、バッファはそれだけのバッファであり、バルク操作でバッファを埋め、それをバッファに渡そうとしているため、個々のバイトのタイプを気にする必要はありません。複雑なデータ構造を解釈し、有用な何かを行うパーサー。その場合は、最も簡単な方法で宣言してください。
マシンのデフォルトロケールの文字列ではなく、実際に8ビットバイトのバッファである場合は、uint8_t
を使用します。 charがバイト(またはバイトがオクテット)ではないマシンの周りに多くのマシンがあるわけではありませんが、「これは文字列です」ではなく「これはオクテットのバッファ」というステートメントを作成することは、多くの場合有用なドキュメントです。
charまたはnsigned charのいずれかを使用する必要がありますが、signed charは使用しないでください。標準には、3.9/2で次のものが含まれています。
PODタイプTの任意のオブジェクト(基本クラスサブオブジェクト以外)の場合、オブジェクトがタイプTの有効な値を保持しているかどうかに関係なく、オブジェクトを構成する基になるバイト(1.7)をcharまたはunsignedの配列にコピーできます。 char。charまたはunsigned charの配列の内容がオブジェクトにコピーされる場合、オブジェクトはその後元の値を保持します。
Unsigned charとして定義することをお勧めします。事実上のWin32型BYTEは、unsigned charとして定義されています。 CとC++の間で違いはありません。
移植性を最大にするために、常にunsigned charを使用してください。これが影響を与える可能性がある場合がいくつかあります。エンディアンの種類が異なるシステム間で共有されるシリアル化されたデータがすぐに思い浮かびます。シフトまたはビットマスキングを実行する場合、値は別です。
Int8_tとuint8_tの選択は、ptrをNULLと比較する場合と同様です。
機能の観点から見ると、NULLは0の#defineであるため、NULLとの比較は0との比較と同じです。
しかし、個人的には、コーディングスタイルの観点から、ポインタをNULLと比較することを選択します。これは、NULL #defineが、不正なポインタをチェックしているコードを保守している人に暗示されるためです...
VS
誰かが0との比較を見るとき、それはあなたが特定の値をチェックしていることを意味します。
上記の理由から、私はuint8_tを使用します。
要素をより広い変数にフェッチすると、もちろんそれは符号拡張されるかどうかです。
すべきであるとすべき...私はする傾向があります 好む データのバイナリ性を強調したい場合は、「生」のように感じられるため、「ちょっと、それは小さなints
の束にすぎません」と言うのにあまり魅力的ではありません。
バイトのバッファを表すために明示的なsigned char
を使用したことはないと思います。
もちろん、3番目のオプションは、バッファを可能な限りvoid *
として表すことです。多くの一般的なI/O関数はvoid *
で機能するため、使用する整数型の決定を完全にカプセル化できる場合があります。これは素晴らしいことです。
数年前に、C++コンソールアプリケーションでASCIIの値が128を超える値を出力するという問題がありました。これは、charからunsigned charに切り替えることで解決しましたが、解決できると思いました。 char型も保持します。
今のところ、ほとんどのC/C++関数はcharを使用しており、両方の言語を理解することができたので、ほとんどの場合、charを使用しています。
本当に気にする?そうでない場合は、デフォルト(char)を使用し、重要でない問題でコードを乱雑にしないでください。そうしないと、将来のメンテナは、なぜ署名付き(または署名なし)を使用したのか疑問に思うでしょう。彼らの生活をよりシンプルにします。