文字エンコードまたはバイナリバッファで動作する一部のライブラリのように、_unsigned char
_を使用してバイナリデータを保持する必要は本当にありますか?私の質問を理解するために、以下のコードを見てください-
_char c[5], d[5];
c[0] = 0xF0;
c[1] = 0xA4;
c[2] = 0xAD;
c[3] = 0xA2;
c[4] = '\0';
printf("%s\n", c);
memcpy(d, c, 5);
printf("%s\n", d);
_
_printf's
_は両方とも_????
_を正しく出力します。ここで_f0 a4 ad a2
_は、UnicodeコードポイントU+24B62 (????)
の16進数のエンコードです。
memcpy
でも、charが保持するビットを正しくコピーしました。
_unsigned char
_の代わりに_plain char
_を使用することを推奨できる理由は何ですか?
他の関連する質問では、_unsigned char
_が強調表示されます。これは、C仕様によってパディングがないことが保証されている唯一の(バイト/最小)データ型であるためです。しかし、上記の例が示したように、出力はパディング自体の影響を受けないようです。
VC++ Express 2010とMinGWを使用して上記をコンパイルしました。 VCは警告を発した
_warning C4309: '=' : truncation of constant value
_
出力はそれを反映していないようです。
追伸これは、 バイトバッファを符号付きまたは符号なしcharバッファにする必要がありますか? の重複の可能性があるとマークできますが、私の意図は異なります。私はなぜchar
でうまく動作しているように見えるものを_unsigned char
_とタイプする必要があるのかと尋ねています。
更新:N3337から引用するには、
_Section 3.9 Types
_
2簡単にコピー可能なタイプTのオブジェクト(ベースクラスサブオブジェクト以外)について、オブジェクトがタイプTの有効な値を保持しているかどうかにかかわらず、オブジェクトを構成する基本バイト(1.7)をcharの配列にコピーできますまたはunsigned char。 charまたはunsigned charの配列の内容がオブジェクトにコピーされた場合、オブジェクトは元の値を保持します。
上記の事実と、私の元の例がchar
がデフォルトの_signed char
_であるIntelマシン上にあったことを考えると、char
よりも_unsigned char
_を優先すべきかどうかはまだわかりません。
他に何か?
Cでは、unsigned char
データ型は、次の3つのプロパティをすべて同時に持つ唯一のデータ型です
これらが探している「バイナリ」データ型のプロパティである場合、unsigned char
を確実に使用する必要があります。
2番目のプロパティには、unsigned
である型が必要です。これらのすべての変換は、モジュロ算術で定義されます。ここでは、ほとんどのアーキテクチャのUCHAR_MAX+1
、256
を法としてモジュロします。これにより、より広い値からunsigned char
へのすべての変換は、最下位バイトへの切り捨てに対応します。
通常、他の2つの文字タイプは同じようには機能しません。 signed char
はとにかく署名されるので、それに適合しない値の変換は明確に定義されていません。 char
は署名済みまたは署名なしに固定されていませんが、コードが移植される特定のプラットフォームでは、署名されていない場合でも署名される可能性があります。
個々のバイトの内容を比較すると、ほとんどの問題が発生します。
char c[5];
c[0] = 0xff;
/*blah blah*/
if (c[0] == 0xff)
{
printf("good\n");
}
else
{
printf("bad\n");
}
コンパイラによっては、c [0]が-1に符号拡張されるため、「bad」を出力できますが、これは0xffとはまったく異なります。
プレーンchar
型は問題があり、文字列以外には使用しないでください。 char
の主な問題は、署名されているか署名されていないかがわからないことです。これは実装定義の動作です。これにより、char
はint
などと異なり、int
は常に署名されていることが保証されます。
VCは警告を出しました...定数値の切り捨て
これは、char変数内にintリテラルを保存しようとしていることを示しています。これは、符号付きに関連している可能性があります。符号付き文字内に値> 0x7Fの整数を格納しようとすると、予期しないことが起こる可能性があります。正式には、これはCでの未定義の動作ですが、(符号付き)char内に格納された整数値として結果を印刷しようとすると、実際には奇妙な出力が得られます。
この特定のケースでは、警告は重要ではありません。
編集:
他の関連する質問では、C仕様によってパディングがないことが保証されている唯一の(バイト/最小)データ型であるため、unsigned charが強調表示されます。
理論的には、C11 6.2.6.2に従って、unsigned charおよびsigned charを除くすべての整数型に「パディングビット」を含めることが許可されています。
「符号なしchar以外の符号なし整数型の場合、オブジェクト表現のビットは、値ビットとパディングビットの2つのグループに分けられます(後者は必要ありません)。」
「符号付き整数型の場合、オブジェクト表現のビットは、値ビット、パディングビット、および符号ビットの3つのグループに分けられます。パディングビットは不要です。符号付きcharはパディングビットを持ちません。」
C標準は意図的に曖昧であいまいであり、これらの理論上のパディングビットを許可します。
ただし、C標準以外の現実の世界では、以下が適用されます。
そのため、C標準の理論的なシナリオを回避するためだけに、unsigned charまたはsigned charを使用する本当の理由はありません。
バイトは通常、符号なし8ビット幅整数として意図されています。
現在、charは整数の符号を指定していません。一部のコンパイラではcharが署名され、他のコンパイラではunsignedになります。
あなたが書いたコードにビットシフト演算を追加すると、未定義の動作になります。追加された比較も予期しない結果になります。
char c[5], d[5];
c[0] = 0xF0;
c[1] = 0xA4;
c[2] = 0xAD;
c[3] = 0xA2;
c[4] = '\0';
c[0] >>= 1; // If char is signed, will the 7th bit go to 0 or stay the same?
bool isBiggerThan0 = c[0] > 0; // FALSE if char is signed!
printf("%s\n", c);
memcpy(d, c, 5);
printf("%s\n", d);
コンパイル中の警告について:charが署名されている場合、値0xf0を割り当てようとしていますが、これはsigned char(-128〜+127の範囲)で表すことができないため、符号付きの値にキャストされます(- 16)。
Charをunsignedとして宣言すると、警告が削除され、警告なしでクリーンビルドを作成することは常に有効です。
プレーンchar
型の符号付きは実装定義であるため、実際に文字データ(プラットフォームの文字セットを使用する文字列-通常はASCII)を処理している場合を除き、通常は符号付きを指定することをお勧めしますsigned char
またはunsigned char
を使用して明示的に。
バイナリデータの場合、特にビット単位の演算がデータに対して実行される場合(特にビットシフト、符号なしの型と符号付きの型で同じ動作をしない場合)は、おそらくunsigned char
が最適です。
文字エンコードまたはバイナリバッファで動作する一部のライブラリのように、バイナリデータを保持するためにunsigned charを使用することが本当に必要ですか?
「本当に」必要ですか?番号。
それは非常に良い考えですが、これには多くの理由があります。
この例では、タイプセーフではないprintfを使用しています。つまり、printfは、データ型からではなく、フォーマット文字列からフォーマットキューを取得します。同様に簡単に試すことができます:
printf("%s\n", (void*)c);
...そして結果は同じだったでしょう。 c ++ iostreamで同じことをしようとすると、結果は異なります(cの符号付き度に応じて)。
プレーンな文字の代わりに符号なしの文字の使用を提唱する可能性のある推論は何ですか?
符号なしは、データの最上位ビット(符号なしcharの8番目のビット)が符号を表すことを指定します。明らかにそれを必要としないので、データが符号なしであることを指定する必要があります(「符号」ビットは他のビットの符号ではなくデータを表します)。
私はなぜcharでうまく機能しているように見えるものをunsigned charと入力する必要があるのですか?
標準の意味で「正しくない」ことを行う場合、未定義の動作に依存します。コンパイラーは、今日あなたが望む方法でそれを行うかもしれませんが、明日何をするのかわかりません。 GCCやVC++ 2012の機能がわからない。または、動作が外部要因やデバッグ/リリースコンパイルなどに依存している場合でも、標準の安全なパスを離れるとすぐに問題が発生する可能性があります。
さて、「バイナリデータ」とは何ですか?これはビットの集まりであり、「バイナリデータ」と呼ばれるソフトウェアの特定の部分によって割り当てられた意味はありません。これらのビットのいずれかに特定の意味がないという考えを伝える最も近いプリミティブデータ型は何ですか?おもう unsigned char
。