web-dev-qa-db-ja.com

C ++の暗黙的な変換(符号付き+符号なし)

暗黙の変換に関して、符号なし型オペランドと符号付き型オペランドがあり、符号なしオペランドの型が符号付きオペランドの型と同じ(または大きい)場合、符号付きオペランドが変換されることを理解しています。符号なしに。

そう:

unsigned int u = 10;  
signed int s = -8;

std::cout << s + u << std::endl;

//prints 2 because it will convert `s` to `unsigned int`, now `s` has the value
//4294967288, then it will add `u` to it, which is an out-of-range value, so,
//in my machine, `4294967298 % 4294967296 = 2`

私が理解していないこと-符号付きオペランドの型が符号なしオペランドよりも大きい場合は、次のように読みます。

  • 符号なし型のすべての値が大きい方の型に適合する場合、符号なしオペランドは符号付き型に変換されます

  • 符号なし型の値が大きい方の型に適合しない場合、符号付きオペランドは符号なし型に変換されます

したがって、次のコードでは:

signed long long s = -8;
unsigned int u = 10;
std::cout << s + u << std::endl;

uは、int値がsigned long longに収まるため、signed longlongに変換されますか?

その場合、どのシナリオで小さいタイプの値が大きいタイプの値に適合しませんか?

14
2013Asker

標準からの関連する引用:

5式[expr]

10算術型または列挙型のオペランドを期待する多くの二項演算子は、同様の方法で変換を引き起こし、結果型を生成します。目的は、結果のタイプでもある共通のタイプを生成することです。このパターンは通常の算術変換と呼ばれ、次のように定義されます。

[等号または等号のタイプに関する2つの節は省略]

—それ以外の場合、符号なし整数型のオペランドのランクが他のオペランドの型のランク以上の場合、符号付き整数型のオペランドは符号なし整数型のオペランドの型に変換されます。

—それ以外の場合、符号付き整数型のオペランドの型が符号なし整数型のオペランドの型のすべての値を表すことができる場合、符号なし整数型のオペランドは符号付き整数型のオペランドの型に変換されます。

—それ以外の場合、両方のオペランドは、符号付き整数型のオペランドの型に対応する符号なし整数型に変換されます。

上記の3つの節のそれぞれについて次の3つのケースの例を考えてみましょうsizeof(int) < sizeof(long) == sizeof(long long)(他のケースに簡単に適応できるシステム)

#include <iostream>

signed int s1 = -4;
unsigned int u1 = 2;

signed long int s2 = -4;
unsigned int u2 = 2;

signed long long int s3 = -4;
unsigned long int u3 = 2;

int main()
{
    std::cout << (s1 + u1) << "\n"; // 4294967294
    std::cout << (s2 + u2) << "\n"; // -2 
    std::cout << (s3 + u3) << "\n"; // 18446744073709551614  
}

実例出力あり。

最初の句:同じランクのタイプ。したがって、signed intオペランドはunsigned intに変換されます。これには、(2の補数を使用して)印刷された値を与える値変換が伴います。

2番目の句:符号付き型のランクが高く、(このプラットフォームでは!)符号なし型のすべての値を表すことができるため、符号なしオペランドが符号付き型に変換され、-2が得られます。

3番目の節:符号付き型のランクは再び高くなりますが、(このプラットフォームでは!)符号なし型のすべての値を表すことはできないため、両方のオペランドがunsigned long longに変換され、符号付きオペランドの値変換後に、印刷された値を取得します。

符号なしオペランドが十分に大きい場合(たとえば、これらの例では6)、符号なし整数オーバーフローのため、最終結果は3つの例すべてに対して2になることに注意してください。

(追加)これらのタイプを比較すると、さらに予期しない結果が得られることに注意してください。上記の例1を<で考えてみましょう。

#include <iostream>

signed int s1 = -4;
unsigned int u1 = 2;
int main()
{
    std::cout << (s1 < u1 ? "s1 < u1" : "s1 !< u1") << "\n";  // "s1 !< u1"
    std::cout << (-4 < 2u ? "-4 < 2u" : "-4 !< 2u") << "\n";  // "-4 !< 2u"
}

2uunsignedサフィックスによって明示的にuにされるため、同じルールが適用されます。そして、結果はおそらく、C++ -4 < 2u ..で記述したときに-4 <2を比較したときに期待したものではありません。

22
TemplateRex

signed intunsigned long longに適合しません。したがって、次の変換が行われます:signed int-> unsigned long long

2
sasha.sochka

C++ 11標準では、ここでは大きいタイプや小さいタイプについては説明していません。ランクが低いタイプや高いタイプについて説明しています。

long intunsigned intの場合を考えてみましょう。どちらも、32ビットです。 long intのランクはunsigned intよりも大きいですが、long intunsigned intはどちらも32ビットであるため、long intはのすべての値を表すことはできません。 unsigned int

したがって、最後のケース(C++ 11:5.6p9)に分類されます。

  • それ以外の場合、両方のオペランドは、符号付き整数型のオペランドの型に対応する符号なし整数型に変換されます。

これは、long intunsigned intの両方がunsigned long intに変換されることを意味します。

1
Vaughn Cato