web-dev-qa-db-ja.com

CおよびC ++の算術演算の前に、shortをintに変換する必要があるのはなぜですか?

この質問 から得た回答から、C++はCから算術演算を実行するときにshortintに変換するためのこの要件を継承したようです。 whyについての頭脳これはそもそもCで導入されましたか?なぜこれらの操作をshortとしてしないのですか?

たとえば、(コメント内のdypの提案から引用):

short s = 1, t = 2 ;
auto  x = s + t ;

xのタイプはintになります。

68
dayuloli

国際標準の理論的根拠—プログラミング言語—C のセクション6.3.1.8通常の算術変換を見ると、(今後のエンファシス鉱山):

これらの変換に関する標準の規則は、K&Rの規則のわずかな変更です。変更は、追加されたタイプと値を保持する規則に対応します。 頻繁に正しい答えは言うまでもなく、より小さくて高速なコードが生成されることがあるため、絶対に必要な「より広い」タイプで計算を実行するための明示的なライセンスが追加されました。同じ結果が得られる限り、あたかもルールによって「より狭い」タイプで計算を実行することもできます。明示的なキャストを常に使用して、目的の型の値を取得できます

セクション6.3.1.8 から C99標準案 は、通常の算術変換をカバーし、算術式のオペランドに適用されます。たとえば、セクション 6.5.6加算演算子 は次のとおりです。

両方のオペランドに算術型がある場合、それらに対して通常の算術変換が実行されます。

セクション 6.5.5乗法演算子 にも同様のテキストがあります。shortオペランドの場合、最初にintegerプロモーションがセクション 6.3。 1.1ブール、文字、整数 これは次のことを言います。

Intが元の型のすべての値を表すことができる場合、値はintに変換されます。それ以外の場合は、unsigned intに変換されます。 これらは整数プロモーションと呼ばれます48) 他のすべてのタイプは、整数プロモーションによって変更されません。

Rationalaleまたは国際標準—プログラミング言語—Coninteger Promotionsのセクション6.3.1.1からの議論/は実際にはもっと興味深いです。b/ cを選択的に引用しますが、完全に引用するには長すぎます。

実装は2つの主要なキャンプに分類され、符号なし保存と値保存として特徴付けられます。

[...]

nsigned preserving approachは、2つの小さなunsigned型をunsigned intにプロモートすることを要求します。これは単純なルールであり、実行環境に依存しないタイプを生成します。

値保存アプローチは、その型が元の型のすべての値を適切に表すことができる場合、それらの型をsigned intに昇格するか、そうでない場合、それらの型を符号なしintに昇格することを要求します。したがって、実行環境がshortをintより小さいものとして表す場合、unsigned shortはintになります。それ以外の場合は、unsigned intになります。

符号なし型とより大きな符号付き型との間の暗黙的な変換の一貫性のない動作 が示すように、これはいくつかの場合に予想外の結果をもたらす可能性があります。ほとんどの場合、これにより操作が期待どおりに動作します。

39
Shafik Yaghmour

コードを実行する物理プロセッサアーキテクチャの制限であるため、言語の機能ではありません。 Cのint typerは通常、標準CPUレジスタのサイズです。より多くのシリコンはより多くのスペースと電力を消費するため、多くの場合、「自然なサイズ」のデータ型でのみ演算を行うことができます。これは普遍的に真実ではありませんが、ほとんどのアーキテクチャにはまだこの制限があります。言い換えると、2つの8ビット数を追加する場合、プロセッサで実際に行われるのは、単純なビットマスクまたは別の適切な型変換のいずれかが後に続く何らかのタイプの32ビット演算です。

21
Phonon

shortおよびcharタイプは、標準的な種類の「ストレージタイプ」で考慮されます。つまり、スペースを節約するために使用できるサブサイズですが、サイズのために速度を購入しません。 CPUにとって「不自然」です。

特定のCPUではこれは正しくありませんが、優れたコンパイラーは、たとえばunsigned charに定数を追加し、結果をunsigned charに保存しておけば、unsigned char -> int変換を行う必要がありません。たとえば、g ++では、内部ループ用に生成されたコード

void incbuf(unsigned char *buf, int size) {
    for (int i=0; i<size; i++) {
        buf[i] = buf[i] + 1;
    }
}

ただ

.L3:
    addb    $1, (%rdi,%rax)
    addq    $1, %rax
    cmpl    %eax, %esi
    jg  .L3
.L1:

ここで、符号なしchar加算命令(addb)が使用されていることがわかります。

短い整数間で計算を行い、結果を短い整数に格納する場合も同じことが起こります。

17
6502

リンクされた質問はそれをかなりうまくカバーしているようです:CPUはちょうどそれをカバーしていません。 32ビットCPUには、32ビットレジスタ用にネイティブ算術演算が設定されています。プロセッサは好みのサイズで作業することを好み、このような操作では、小さな値をネイティブサイズのレジスタにコピーするのは安価です。 (x86アーキテクチャの場合、32ビットレジスタは、16ビットレジスタの拡張バージョンであるかのように名前が付けられます(eaxからaxebxからbxなど); x86整数命令 )を参照してください。

一部の非常に一般的な演算、特にベクトル/浮動小数点演算では、異なるレジスタタイプまたはサイズで動作する特殊な命令が存在する場合があります。短いもののように、(最大)16ビットのゼロでパディングすることはパフォーマンスコストが非常に少なく、特殊な命令を追加することはおそらくダイ上の時間またはスペースの価値がありません(理由を本当に物理的に知りたい場合は、私は実際のスペースを取るかどうかはわかりませんが、もっと複雑になります)。

7
ssube