符号付きintと符号なしintは同じレジスタなどを使用し、ビットパターンの解釈が異なるだけで、C文字は基本的に8ビットintであるとすると、Cの符号付き文字と符号なし文字の違いは何ですか?charの符号は実装で定義されていることを理解しています。少なくとも、charが数学ではなく文字列を保持するために使用されている場合、それがどのように違いを生むことができるかを理解できません。
9 に答える
文字列には違いはありません。しかし、Cでは、charを使用して数学を行うことができます。
実際、組み込みの8ビットアプリケーションのような制約のあるメモリ環境で作業する場合、charは数学を行うために使用されることが多く、大きな違いがあります。byte
これは、Cにはデフォルトで型がないためです。
それらが表す値に関して:
unsigned char:
- 値の範囲にまたがる
0..255 (00000000..11111111)
値は次のようにローエッジの周りにオーバーフローします。
0 - 1 = 255 (00000000 - 00000001 = 11111111)
値は次のようにハイエッジの周りにオーバーフローします。
255 + 1 = 0 (11111111 + 00000001 = 00000000)
ビット単位の右シフト演算子(
>>
)は論理シフトを実行します。10000000 >> 1 = 01000000 (128 / 2 = 64)
署名された文字:
- 値の範囲にまたがる
-128..127 (10000000..01111111)
値は次のようにローエッジの周りにオーバーフローします。
-128 - 1 = 127 (10000000 - 00000001 = 01111111)
値は次のようにハイエッジの周りにオーバーフローします。
127 + 1 = -128 (01111111 + 00000001 = 10000000)
ビット単位の右シフト演算子(
>>
)は算術シフトを実行します。10000000 >> 1 = 11000000 (-128 / 2 = -64)
値のラッピング動作が純粋で一貫性のあるバイナリ演算であり、文字が符号付き/符号なしであることとは関係がないことを示すために、バイナリ表現を含めました(右シフトを想定)。
アップデート
コメントで言及されているいくつかの実装固有の動作:
- char!=符号付きchar。「signed」または「unsinged」のない型「char」は実装定義であり、これは、符号付きまたは符号なしの型のように機能できることを意味します。
- 符号付き整数のオーバーフローは、コアのダンプやバッファのオーバーランなど、プログラムが何でも実行できる未定義の動作につながります。
#include <stdio.h>
int main(int argc, char** argv)
{
char a = 'A';
char b = 0xFF;
signed char sa = 'A';
signed char sb = 0xFF;
unsigned char ua = 'A';
unsigned char ub = 0xFF;
printf("a > b: %s\n", a > b ? "true" : "false");
printf("sa > sb: %s\n", sa > sb ? "true" : "false");
printf("ua > ub: %s\n", ua > ub ? "true" : "false");
return 0;
}
[root]# ./a.out
a > b: true
sa > sb: true
ua > ub: false
文字列を並べ替えるときに重要です。
いくつかの違いがあります。最も重要なことは、大きすぎるか小さすぎる整数を char に割り当てることによって char の有効範囲をオーバーフローし、char が符号付きである場合、結果の値は実装で定義されるか、すべての符号付き型と同様に (C で) 何らかのシグナルが発生する可能性があることです。 . unsigned char に大きすぎたり小さすぎたりするものを割り当てる場合とは対照的です。値がラップアラウンドすると、正確に定義されたセマンティクスが得られます。たとえば、unsigned char に -1 を割り当てると、UCHAR_MAX が得られます。したがって、0 から 2^CHAR_BIT までの数値のようなバイトがある場合は、unsigned char を使用して格納する必要があります。
符号は、vararg 関数に渡すときにも違いを生みます。
char c = getSomeCharacter(); // returns 0..255
printf("%d\n", c);
c に割り当てられた値が大きすぎて char で表すことができず、マシンが 2 の補数を使用するとします。多くの実装は、ビットパターンが変更されないという点で、char に大きすぎる値を割り当てた場合に動作します。int が char のすべての値を表すことができる場合 (ほとんどの実装ではそうです)、char は printf に渡す前に int に昇格されます。したがって、渡される値は負になります。int に昇格すると、その符号が保持されます。したがって、否定的な結果が得られます。ただし、char が unsigned の場合、値は unsigned であり、int に昇格すると正の int が生成されます。unsigned char を使用すると、変数への代入と printf への受け渡しの両方で正確に定義された動作が得られ、肯定的な結果が出力されます。
char、unsigned、signed char はすべて、少なくとも8 ビット幅であることに注意してください。char が正確に8 ビット幅である必要はありません。ただし、ほとんどのシステムではそうですが、一部のシステムでは 32 ビット文字を使用していることがわかります。C および C++ のバイトは char のサイズを持つように定義されているため、C のバイトも必ずしも正確に 8 ビットではありません。
もう 1 つの違いは、C では unsigned char にパディング ビットが含まれていてはならないことです。つまり、CHAR_BIT が 8 の場合、unsigned char の値は 0 から 2^CHAR_BIT-1 の範囲でなければなりません。char が unsigned の場合も同様です。signed char の場合、コンパイラが符号 (2 の補数またはその他のオプション) を実装する方法を知っていても、値の範囲については何も想定できません。未使用のパディング ビットが含まれている可能性があります。C++ では、3 つの文字型すべてにパディング ビットはありません。
「charが署名されるとはどういう意味ですか?」
従来、ASCII文字セットは7ビットの文字エンコーディングで構成されていました。(8ビットEBCIDICとは対照的です。)
C言語が設計および実装されたとき、これは重要な問題でした。(シリアルモデムデバイスを介したデータ伝送などのさまざまな理由で。)余分なビットには、パリティなどの用途があります。
「署名された文字」は、たまたまこの表現に最適です。
バイナリデータOTOHは、データの各8ビット「チャンク」の値を取得するだけなので、符号は必要ありません。
バイトの算術演算は、コンピューター グラフィックス (色の保存に 8 ビット値がよく使用される) にとって重要です。それとは別に、char 記号が重要な 2 つの主なケースを考えることができます。
- より大きな int への変換
- 比較関数
厄介なことに、すべての文字列データが 7 ビットの場合、これらは問題になりません。ただし、C/C++ プログラムを 8 ビット クリーンにしようとしている場合は、あいまいなバグが後を絶たない可能性があります。
符号付き文字についての1つのことは、c> =''(スペース)をテストして、それが通常の印刷可能なASCII文字であることを確認できることです。もちろん、持ち運びはできないので、あまり役に立ちません。
これが問題であると私が想像できる唯一の状況は、文字で数学を行うことを選択した場合です。次のコードを書くことは完全に合法です。
char a = (char)42;
char b = (char)120;
char c = a + b;
charの符号に応じて、cは2つの値のいずれかになります。charが符号なしの場合、cは(char)162になります。署名されている場合、署名された文字の最大値は128であるため、オーバーフローケースになります。ほとんどの実装は(char)-32を返すだけだと思います。