56

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出力されます。ここで、は16進数のUnicodeコードポイントのエンコーディングです。f0 a4 ad a2U+24B62 ()

memcpycharが保持するビットも正しくコピーしました。

unsigned charの代わりにの使用を提唱する可能性のある理由は何plain charですか?

他の関連する質問unsigned charでは、C仕様によってパディングがないことが保証されている唯一の(バイト/最小)データ型であるため、強調表示されています。しかし、上記の例が示しているように、出力はパディング自体の影響を受けていないようです。

上記をコンパイルするためにVC++Express2010とMinGWを使用しました。VCは警告を出しましたが

warning C4309: '=' : truncation of constant value

出力はそれを反映していないようです。

PSこれは重複の可能性があるとマークされる可能性がありますバイトのバッファは符号付きまたは符号なしのcharバッファですか?しかし、私の意図は異なります。うまく機能しているように見えるものをchar入力する必要があるのはなぜunsigned charですか?

更新: N3337から引用するには、

Section 3.9 Types

2自明にコピー可能なタイプTのオブジェクト(基本クラスのサブオブジェクトを除く)の場合、オブジェクトがタイプTの有効な値を保持しているかどうかに関係なく、オブジェクトを構成する基になるバイト(1.7)をcharの配列にコピーできます。またはunsignedchar。charまたはunsignedcharの配列の内容がオブジェクトにコピーバックされた場合、オブジェクトはその後元の値を保持する必要があります。

上記の事実と、私の元の例がcharデフォルトでに設定されているIntelマシン上にあったことを考慮すると、よりも優先されるべきsigned charかどうかはまだ確信が持てません。unsigned charchar

他に何か?

4

8 に答える 8

99

C では、unsigned charデータ型は、次の 3 つのプロパティすべてを同時に持つ唯一のデータ型です。

  • パディング ビットがなく、すべてのストレージ ビットがデータの値に寄与する
  • その型の値から始まるビット演算は、その型に変換されたときに、オーバーフロー、トラップ表現、または未定義の動作を生成することはありません
  • 「エイリアスルール」に違反することなく、他のデータ型をエイリアスすることができます。つまり、異なる型のポインターを介して同じデータにアクセスすると、すべての変更が表示されることが保証されます。

これらが探している「バイナリ」データ型のプロパティである場合は、間違いなく を使用する必要がありますunsigned char

2 番目のプロパティには、型が必要ですunsigned。これらのすべての変換は、アーキテクチャのほとんど 99% でモジュロ算術 (ここでは modulo ) で定義されていUCHAR_MAX+1ます。256したがって、より広い値のすべての変換はunsigned char、最下位バイトへの切り捨てに対応します。

通常、他の 2 つの文字タイプは同じようには機能しません。signed charとにかく、署名されているため、それに適合しない値の変換は明確に定義されていません。char署名付きまたは署名なしに固定されていませんが、コードが移植された特定のプラットフォームでは、コードが署名されていなくても署名される場合があります。

于 2012-11-30T10:06:26.977 に答える
16

個々のバイトの内容を比較すると、ほとんどの問題が発生します。

char c[5];
c[0] = 0xff;
/*blah blah*/
if (c[0] == 0xff)
{
    printf("good\n");
}
else
{
    printf("bad\n");
}

コンパイラによっては、c[0] が -1 に符号拡張され、0xff とまったく同じではないため、「不良」と出力される可能性があります。

于 2012-11-30T10:46:50.397 に答える
12

プレーンcharタイプは問題があり、文字列以外には使用しないでください。の主な問題charは、署名されているか署名されていないかがわからないことです。これは実装定義の動作です。これはetc とはchar異なり、常に署名されることが保証されています。intint

VCは警告を出しましたが...定数値の切り捨て

char 変数内に int リテラルを格納しようとしていることを示しています。これは符号に関連している可能性があります: 値 > 0x7F の整数を符号付き文字内に格納しようとすると、予期しないことが起こる可能性があります。正式には、これは C では未定義の動作ですが、(signed) char 内に格納された整数値として結果を出力しようとすると、実際には奇妙な出力が得られます。

この特定のケースでは、警告は重要ではありません。

編集 :

他の関連する質問では、unsigned char が強調表示されています。これは、C 仕様によってパディングがないことが保証されている唯一の (バイト/最小) データ型であるためです。

理論的には、C11 6.2.6.2 に従って、unsigned char と signed char を除くすべての整数型に「パディング ビット」を含めることができます。

「unsigned char 以外の符号なし整数型の場合、オブジェクト表現のビットは、値ビットとパディング ビットの 2 つのグループに分割されます (後者のいずれかが存在する必要はありません)。」

「符号付き整数型の場合、オブジェクト表現のビットは、値ビット、パディング ビット、符号ビットの 3 つのグループに分けられます。パディング ビットは必要ありません。符号付き char にはパディング ビットはありません。」

C 標準は意図的にあいまいでファジーであり、次の理由により、これらの理論的なパディング ビットを許可します。

  • 標準の 8 ビットのものとは異なるシンボル テーブルを使用できます。
  • これにより、実装定義の符号性と、1 の補数や「符号と大きさ」などの奇妙な符号付き整数形式が可能になります。
  • 整数は、割り当てられたすべてのビットを必ずしも使用するとは限りません。

ただし、C 標準以外の現実の世界では、次のことが当てはまります。

  • シンボル テーブルはほぼ確実に 8 ビット (UTF8 または ASCII) です。奇妙な例外がいくつかありますが、クリーンな実装では、8 ビットを超えるシンボル テーブルを実装するときに標準型wchar_tを使用します。
  • 符号は常に 2 の補数です。
  • 整数は、割り当てられたすべてのビットを常に使用します。

したがって、C 標準の理論的なシナリオを回避するためだけに unsigned char または signed char を使用する本当の理由はありません。

于 2012-11-30T09:46:18.090 に答える
8

バイトは通常、符号なしの 8 ビット幅の整数として意図されています。

現在、 char は整数の符号を指定していません: 一部のコンパイラでは char は署名されている可能性があり、他のコンパイラでは符号なしである可能性があります。

あなたが書いたコードにビットシフト操作を追加すると、未定義の動作が発生します。追加された比較も予期しない結果になります。

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 が署名されている場合、signed char (範囲 -128 から +127) で表すことができない値 0xf0 を割り当てようとしているため、signed 値 (- 16)。

char を unsigned として宣言すると、警告が削除され、警告なしでクリーン ビルドを行うことは常に適切です。

于 2012-11-30T10:13:38.177 に答える
4

プレーン型の符号の有無は実装で定義されるため、実際に文字データ (プラットフォームの文字セットを使用した文字列 - 通常は ASCII) を扱っている場合を除き、通常はまたはcharを使用して符号の有無を明示的に指定することをお勧めします。signed charunsigned char

unsigned charバイナリ データの場合、特にデータに対してビット単位の操作が実行される場合 (特に、符号付きの型と符号なしの型とで動作が異なるビット シフト) の場合、最適な選択はおそらくです。

于 2012-11-30T09:45:34.820 に答える
2

文字エンコードまたはバイナリバッファで動作する一部のライブラリのように、バイナリデータを保持するためにunsigned charを使用する必要が本当にありますか?

「本当に」必要ですか?いいえ。

しかし、それは非常に良い考えであり、これには多くの理由があります。

あなたの例では、タイプセーフではないprintfを使用しています。つまり、printfは、データ型からではなく、フォーマット文字列からフォーマットキューを取得します。あなたは同じように簡単に試すことができます:

printf("%s\n", (void*)c);

...そして結果は同じだったでしょう。c ++ iostreamで同じことを試してみると、結果は異なります(cの符号によって異なります)。

プレーン文字の代わりにunsignedcharの使用を提唱する可能性がある理由は何ですか?

Signedは、データの最上位ビット(unsigned charの場合は8番目のビット)が符号を表すことを指定します。明らかにそれは必要ないので、データが符号なしであることを指定する必要があります(「符号」ビットはデータを表し、他のビットの符号ではありません)。

于 2012-11-30T10:57:20.550 に答える
2

char で問題なく動作しているように見えるものを unsigned char と入力する必要があるのはなぜですか?

標準の意味で「正しくない」ことを行うと、未定義の動作に依存します。あなたのコンパイラは、今日はあなたが望むようにそれを行うかもしれませんが、明日はどうなるかわかりません。GCC や VC++ 2012 が何をしているのかわかりません。または、動作が外部要因やデバッグ/リリース コンパイルなどに依存している場合でも、標準の安全なパスを離れるとすぐに、問題が発生する可能性があります。

于 2012-11-30T09:44:47.953 に答える
2

では、「バイナリ データ」とは何と呼ぶのでしょうか。これはビットの集まりであり、「バイナリデータ」と呼ばれるソフトウェアの特定の部分によって割り当てられた意味はありません。これらのビットのいずれにも特定の意味がないという考えを伝える、最も近いプリミティブ データ型は何ですか? と思いますunsigned char

于 2012-11-30T09:46:20.683 に答える