整数型 解答ページ | Programming Place Plus C言語編 第19章

トップページC言語編第19章

問題①

問題① 次の変数 n1~n8 の型はそれぞれ何が適切ですか?(あえて大きい型を使わないとして)

signed char sc = -10;
unsigned short us = 10;

???? n1 = 10L;
???? n2 = 10LL;
???? n3 = 10LU;
???? n4 = 10 + n1;
???? n5 = n2 + n3;
???? n6 = sc * 2;
???? n7 = sc * us;
???? n8 = sc + 100LL / 2U;


n1 の初期化子は 10L です。整数リテラルに Lサフィックスを付加した場合、long int になります(表現できない値なら long long int や拡張整数型になるかもしれないが、10 は表現できる)。したがって、n1 の型は long int です。

n2 では 10LL を指定しています。整数リテラルに LLサフィックスを付加した場合、long long int になります(表現できない値なら拡張整数型になるかもしれないが、10 は表現できる)。したがって、n2 の型は long long int です。

n3 では 10LU を指定しています。LU は、Lサフィックスと Uサフィックスを両方指定したものです。Uサフィックスは符号無し整数型にすることを表しているので、n3 の型は unsigned long int です。

n4 では、10 + n1 を指定しています。10 は int、n1 は long int でした。したがってこれは、int と long int で計算を行っています。オペランドが2つある演算子で互いの型が異なる場合、通常の算術変換が適用されます。「両方とも signed、あるいは両方とも unsigned である場合、ランクが低い側が、ランクが高い側の型に変換される」の部分が適用されるので、long int に合わせられます。したがって、4 は long int です。

n5 では、n2 + n3 を指定しています。n2 は long long int、n3 は unsigned long int です。さきほどと同様に一方の型に合わせる変換が起こります。long long int が 64ビット、unsigned long int が 32ビットの処理系であれば、「signed の側の型が、unsigned の側の型で表現できるすべての値を表現できるのなら、unsigned の側が signed の型に変換される」に該当するので、n5 は long long int になります。long long int と unsigned long int がともに 64ビットの処理系であれば、「両方とも、signed の側の型に対応する unsigned な型に変換される」に該当するので、n5 は unsigned long long int になります。

n6 では、sc * 2 を指定しています。sc は signed char、2 は int です。signed char は int よりもランクが低いため、まず整数拡張が行われ、int に変換されます。そのため、sc * 2 は int 同士での演算になり、その結果もまた int です。したがって、n6 は int です。

n7 では、sc * us を指定しています。sc は signed char、us は unsigned short です。さきほどと同様、それぞれに整数拡張が行われ、sc は int になります。unsigned short からの整数拡張は、unsigned short で表現できる値のすべてが int でも表現できるなら int、表現できない値があるのなら unsigned int になります。したがって、unsigned short が 16ビット、int が 32ビットの処理系ならば int になります。いずれも 16ビットであるような処理系では unsigned int になるでしょう。したがって、sc * us は int と int あるいは、int と unsigned int の乗算になります。前者なら n7 は int、後者なら「unsigned の側のランクが signed の側のランク以上の場合、signed の側が unsigned の側の型に変換される」が適用されるので、n7 は unsigned int になります。

n8 では、sc + 100LL / 2U を指定しています。sc は signed char、100LL は long long int、2U は unsigned int です。まず 100LL / 2U が計算されます。long long int が 64ビット、unsigned int が 32ビットとすると、「signed の側の型が、unsigned の側の型で表現できるすべての値を表現できるのなら、unsigned の側が signed の型に変換される」が適用されて、long long int に合わせられます。sc は整数拡張されて int になり、int と long long int の加算を行うことになり、「両方とも signed、あるいは両方とも unsigned である場合、ランクが低い側が、ランクが高い側の型に変換される」が適用されるので、n8 は long long int です。もし、100LL / 2U のとき、long long int と unsigned int が 64ビットであったとすれば、「両方とも、signed の側の型に対応する unsigned な型に変換される」が適用され、両方とも unsigned long long int に変換されます。そうなると、sc との加算は int と unsigned long long int の演算になるので、「unsigned の側のランクが signed の側のランク以上の場合、signed の側が unsigned の側の型に変換される」が適用され、n8 は unsigned long long int になります。

問題②

問題② char型が符号付きか、符号無しかを判定するプログラムを作成してください。


方法はいくつかありえるかもしれませんが、CHAR_MIN が負数かどうかを調べるのが簡単でしょう。

#include <limits.h>
#include <stdio.h>

int main(void)
{
    if (CHAR_MIN < 0) {
        puts("signed.");
    }
    else{
        puts("unsigned.");
    }
}

実行結果:

signed.

問題③

問題③ 次のプログラム片を見てください。

signed char c1 = 120;
signed char c2 = 60;
signed char c3 = -100;
signed char result = c1 + c2 + c3;

signed char型で表現できる最大値は 127 であり、c1 + c2 の段階で溢れ出してしまうように思えます。実際には、最終的な result の値は 80 となり正しく計算できます。問題が起こらない理由を説明してください。


signed char型どうしの計算は、整数拡張によって int型に拡張されます。したがって、120 + 60 + (-100) という計算は int型で行われます。int型の大きさは少なくとも 16ビットはあるので、この程度の大きさの計算では溢れません。

計算結果の 80 は、signed char型の変数result へ代入する際に切り詰められます。しかし、80 であれば signed char型でも表現できるので、ここでも何も問題は起こりません。


参考リンク


更新履歴

’2018/6/5 第20章から整数型に関係する内容を移動してくる形で、新規作成。



第19章のメインページへ

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る