19

私は簡単なプログラムを持っています。サイズが 1 バイトの符号なし固定幅整数を使用していることに注意してください。

#include <cstdint>
#include <iostream>
#include <limits>

int main()
{
    uint8_t x = 12;
    std::cout << (x << 1) << '\n';
    std::cout << ~x;

    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin.get();

    return 0;
}

私の出力は次のとおりです。

24
-13

より大きな数値をテストしたところ、オペレーター<<は常に正の数値を返しますが、オペレーター~は常に負の数値を返します。それから私は使用sizeof()して見つけました...

左シフトのビット演算子 ( <<) を使用すると、符号なしの 4 バイト整数が返されます。

ビット単位の not operator( ~) を使用すると、符号付き 4 バイト整数を受け取ります。

ビットごとの not operator( ~) は、算術演算子のように符号付き整数昇格を行うようです。ただし、左シフト operator( <<) は符号なし整数に昇格するようです。

コンパイラが背後で何かを変更していることを知る義務があると感じています。私の分析が正しければ、すべてのビット演算子は 4 バイト整数に昇格しますか? また、署名付きのものと署名なしのものがあるのはなぜですか? 私は困惑している!

編集:常に正の値または常に負の値を取得するという私の仮定は間違っていました。しかし、間違っていたので、以下の素晴らしい回答のおかげで、実際に何が起こっていたのか理解できます.

4

4 に答える 4

13

[expr.unary.op]

のオペランドは、~整数型またはスコープなしの列挙型を持つ必要があります。結果はそのオペランドの 1 の補数です。インテグラルプロモーションが実行されます。

[expr.shift]

シフト演算子<<>>グループは左から右へ。[...] オペランドは、整数またはスコープなしの列挙型である必要があり、整数昇格が実行されます。

不可欠なプロモーションは何ですかuint8_t(通常unsigned_charは舞台裏で行われます)?

[コンバージョンプロム]

boolchar16_tchar32_t、または wchar_t整数変換ランク (4.13) が のランクより小さい 整数型のprvalueは、ソース型のすべての値を表すことができる場合int、型の prvalue に変換できます。それ以外の場合は、ソースの prvalue を type の prvalue に変換できます。intintunsigned int

つまりint、a のすべての値は でuint8_t表すことができるからintです。

とはint(12) << 1? int(24).

とは~int(12)? int(-13).

于 2015-05-27T05:51:16.870 に答える
5

パフォーマンス上の理由から、C および C++ 言語intは「最も自然な」整数型であると見なし、代わりに「小さい」型をint一種の「ストレージ」型と見なします。

式でストレージ型を使用すると、暗黙的にintまたは に自動的に変換されます。unsigned int例えば:

// Assume a char is 8 bit
unsigned char x = 255;
unsigned char one = 1;

int y = x + one; // result will be 256 (too large for a byte!)
++x;             // x is now 0

何が起こったのかというxone、最初の式で and が暗黙的に整数に変換され、加算が計算され、結果が整数に戻されたということです。つまり、計算は 2 つの unsigned char を使用して実行されていません。

同様floatに、式に値がある場合、コンパイラが最初に行うことは、それを に昇格させることですdouble(つまりfloat、ストレージ タイプであり、double代わりに浮動小数点数の自然なサイズになります)。これが、フロートを出力するために使用する場合、フォーマット文字列を intprintfと言う必要がなく、十分である理由です (ただし、その関数は結果を格納し、 aは a よりも小さくなる可能性があるため、が必要です)。%lf%f%lfscanffloatdouble

intC++ では、関数にパラメーターを渡すときにs と小さい型を区別できるため、問題がかなり複雑になりました。したがって、すべての式で変換が実行されるとは限りません...たとえば、次のようにすることができます。

void foo(unsigned char x);
void foo(int x);

どこ

unsigned char x = 255, one = 1;
foo(x);       // Calls foo(unsigned char), no promotion
foo(x + one); // Calls foo(int), promotion of both x and one to int
于 2015-05-27T06:08:02.367 に答える
4

より大きな数値をテストしたところ、演算子 << は常に正の数値を返し、演算子 ~ は常に負の数値を返します。次に、 sizeof() を使用して見つけました...

間違っています、テストしてください:

uint8_t v = 1;
for (int i=0; i<32; i++) cout << (v<<i) << endl;

与えます:

1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
-2147483648

uint8_tは、範囲 [0,255] の値を表すことができる 8 ビット長の符号なし整数型intです。への昇格は への昇格よりも優先されます。intunsigned intintunsigned

于 2015-05-27T06:05:07.823 に答える
3

2 の補数と、コンピューターが負の整数を格納する方法を調べます。
これを試して

#include <cstdint>
#include <iostream>
#include <limits>
int main()
{
uint8_t x = 1;
int shiftby=0;
shiftby=8*sizeof(int)-1;
std::cout << (x << shiftby) << '\n'; // or std::cout << (x << 31) << '\n';

std::cout << ~x;

std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
return 0;
}

出力は -2147483648 です

一般に、符号付き数値の最初のビットが 1 の場合、それは負と見なされます。あなたが大きな数を取り、それをシフトするとき。最初のビットが1になるようにシフトすると、負になります

**編集**
シフト演算子がunsigned intを使用する理由は思いつきます。>>-12 を右シフトすると、-6 の代わりに 122 が得られますこれは、符号を考慮せずに先頭にゼロを追加するためです。

于 2015-05-27T05:39:58.217 に答える