web-dev-qa-db-ja.com

バイトのバッファーは、署名付きまたは署名なしのcharバッファーにする必要がありますか?

バイトのバッファは、signed char、unsigned char、または単にcharバッファにする必要がありますか? CとC++の違いは何ですか?

ありがとう。

54
jackhab

バイトのバッファは、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に変更するだけで、問題を修正できました。

33
dan04

任意のバイナリデータを格納する場合は、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_MAX2^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が最適です。

ほとんどの実際のケースでは、バッファはそれだけのバッファであり、バルク操作でバッファを埋め、それをバッファに渡そうとしているため、個々のバイトのタイプを気にする必要はありません。複雑なデータ構造を解釈し、有用な何かを行うパーサー。その場合は、最も簡単な方法で宣言してください。

12
RBerteig

マシンのデフォルトロケールの文字列ではなく、実際に8ビットバイトのバッファである場合は、uint8_tを使用します。 charがバイト(またはバイトがオクテット)ではないマシンの周りに多くのマシンがあるわけではありませんが、「これは文字列です」ではなく「これはオクテットのバッファ」というステートメントを作成することは、多くの場合有用なドキュメントです。

9
Pete Kirkham

charまたはnsigned charのいずれかを使用する必要がありますが、signed charは使用しないでください。標準には、3.9/2で次のものが含まれています。

PODタイプTの任意のオブジェクト(基本クラスサブオブジェクト以外)の場合、オブジェクトがタイプTの有効な値を保持しているかどうかに関係なく、オブジェクトを構成する基になるバイト(1.7)をcharまたはunsignedの配列にコピーできます。 char。charまたはunsigned charの配列の内容がオブジェクトにコピーされる場合、オブジェクトはその後元の値を保持します。

5
Richard Corden

Unsigned charとして定義することをお勧めします。事実上のWin32型BYTEは、unsigned charとして定義されています。 CとC++の間で違いはありません。

4
Naveen

移植性を最大にするために、常にunsigned charを使用してください。これが影響を与える可能性がある場合がいくつかあります。エンディアンの種類が異なるシステム間で共有されるシリアル化されたデータがすぐに思い浮かびます。シフトまたはビットマスキングを実行する場合、値は別です。

3
MrEvil

Int8_tとuint8_tの選択は、ptrをNULLと比較する場合と同様です。


機能の観点から見ると、NULLは0の#defineであるため、NULLとの比較は0との比較と同じです。

しかし、個人的には、コーディングスタイルの観点から、ポインタをNULLと比較することを選択します。これは、NULL #defineが、不正なポインタをチェックしているコードを保守している人に暗示されるためです...

VS

誰かが0との比較を見るとき、それはあなたが特定の値をチェックしていることを意味します。


上記の理由から、私はuint8_tを使用します。

2

要素をより広い変数にフェッチすると、もちろんそれは符号拡張されるかどうかです。

0
pngaz

すべきであるとすべき...私はする傾向があります 好む データのバイナリ性を強調したい場合は、「生」のように感じられるため、「ちょっと、それは小さなintsの束にすぎません」と言うのにあまり魅力的ではありません。

バイトのバッファを表すために明示的なsigned charを使用したことはないと思います。

もちろん、3番目のオプションは、バッファを可能な限りvoid *として表すことです。多くの一般的なI/O関数はvoid *で機能するため、使用する整数型の決定を完全にカプセル化できる場合があります。これは素晴らしいことです。

0
unwind

数年前に、C++コンソールアプリケーションでASCIIの値が128を超える値を出力するという問題がありました。これは、charからunsigned charに切り替えることで解決しましたが、解決できると思いました。 char型も保持します。

今のところ、ほとんどのC/C++関数はcharを使用しており、両方の言語を理解することができたので、ほとんどの場合、charを使用しています。

0
schnaader

本当に気にする?そうでない場合は、デフォルト(char)を使用し、重要でない問題でコードを乱雑にしないでください。そうしないと、将来のメンテナは、なぜ署名付き(または署名なし)を使用したのか疑問に思うでしょう。彼らの生活をよりシンプルにします。

0
Gorpik