Programming Place Plus トップページ – C言語編 – 第21章
問題① 次の3つの if文の中から、結果が真になるものを選んでください(何個あるかは不明)。
short snum = -10;
long lnum = -10;
if( snum == lnum ){}
if( snum == (short)lnum ){}
if( (unsigned short)snum == -10 ){}
1つ目の if文は、short型と long型の比較です。型が異なる演算なので、通常の算術型変換が行われ、両オペランドともに long型に変換されます。short型の値は、long型でそのまま表現できますから、-10 のまま比較され、結果は真になります。
2つ目の if文は、long型を short型にキャストしてから比較を行っていますから、これは short型同士の比較になります。変数lnum のもともとの値である -10 は、short型に縮小しても、変わらず表現できますから、値もそのまま保たれます。
short型の変換順位は低いため、整数拡張によって int型に拡張されます。ここでも特に情報を失うことはないので、-10 のまま比較され、結果は真になります。
3つ目の if文は非常にややこしいです。大抵の環境では偽になるはずですが、真になる可能性もあるかもしれません。
まず、short型の -10 を unsigned short型にキャストしています。この結果、unsigned short型で表現できる最大に近い正の数になってしまうはずです。
比較は、unsigned short型と int型で行おうとしている訳ですが、unsigned short型は変換順位が低いため、整数拡張で変換されます。ここの変換結果が厄介で、int型の方が short型よりも大きい環境(現代では恐らくこちらが大半でしょう)では、int型に変換されます。しかし、int型と short型の大きさが同じ環境では、unsigned int型に変換されるはずです。いずれにしても、巨大な正の数は、そのままの値を保ちます。
int型に変換された場合は、int 対 int の比較になるため、巨大な正の数と -10 を比較して偽になります。unsigned int型に変換された場合、通常の算術型変換により、unsigned int 対 unsigned int の比較になります。この場合、-10 の方も巨大な正の数に化けてしまいます。すると両者とも、unsigned int型で表現された巨大な同一の正の数になりますから、真になります。
一応、次のように適当なプログラムを書いて試しておきましょう。
#include <stdio.h>
int main(void)
{
short snum = -10;
long lnum = -10;
if( snum == lnum ){
( "1" );
puts}
if( snum == (short)lnum ){
( "2" );
puts}
if( (unsigned short)snum == -10 ){
( "3" );
puts}
}
実行結果:
1
2
問題② 次のプログラムで出力される 3つの値は、それぞれいくつか答えてください。
#include <stdio.h>
int main(void)
{
short snum = 1000;
short num1 = snum + snum;
short num2 = (int)snum + (int)snum;
short num3 = (int)(snum + snum);
( "%hd\n", num1 );
printf( "%hd\n", num2 );
printf( "%hd\n", num3 );
printf}
いずれも 2000 が出力されます。
実行結果:
2000
2000
2000
いずれにせよ、式の中の short型の値は、整数拡張によって、int型に変換されます。 num1 の場合、int型の 2000 を short型に再変換しますが、2000 は short型でも表現できるので問題ありません。
num2 は、整数拡張をキャストで明示的にしただけです。 num1 のケースと同じで、2000 は short型でも表現できるので、問題なく 2000 になります。
num3 の場合、まず snum が整数拡張で int型に変換されます。その計算結果もまた int型ですから、int型へのキャストには特に意味がありません。これも最終的に short型に変換されますが、やはり問題なく 2000 になります。
問題③ 次のプログラムを、出力される値が 0.47 になるように修正してください。 何通りの修正方法が考えられますか?
#include <stdio.h>
int main(void)
{
int num = 47;
( "%f\n", num / 100 );
printf}
まず、このままでは正しく 0.47 が出力されないのは、int型で計算しているからです。 int型の変数num を int型の 100 で割っていますから、その結果は 0 になってしまいます。
出力したい値は 0.47 という浮動小数点数なので、printf関数の変換指定子は変えられません。従って、printf関数に渡す値の方を、浮動小数点型に仕立てます。
次のようにキャストを使う方法が考えられます。
#include <stdio.h>
int main(void)
{
int num = 47;
( "%f\n", (double)num / 100 );
printf}
実行結果:
0.470000
あるいは、次のようにします。
#include <stdio.h>
int main(void)
{
int num = 47;
( "%f\n", num / (double)100 );
printf}
実行結果:
0.470000
いずれも、キャストによって double型に変換しています(float型でも構いません)。 計算の際、一方が double型であれば、他方も double型に拡張され、結果も double型になります。
またはキャストを使わず、定数の 100 を 100.0 と書けば、double型になりますから、これでも構いません。
#include <stdio.h>
int main(void)
{
int num = 47;
( "%f\n", num / 100.0 );
printf}
実行結果:
0.470000
変数num の方を、最初から double型にしておくことも考えられます。
#include <stdio.h>
int main(void)
{
double num = 47.0;
( "%f\n", num / 100 );
printf}
ここまでに挙げたどの方法も、別段、どれが優れているということはありません。 また、組み合わせても構いません。 いろいろとやり方があるということが理解できれば良いでしょう。
問題④ 次のプログラム片を見てください。
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型でも表現できるので、ここでも何も問題は起こりません。
return 0;
を削除(C言語編全体でのコードの統一)’2018/6/4 第30章から練習問題⑩を移動してきて、練習問題④とした。
’2018/2/23 全面的に文章を見直し、修正を行った。
’2018/1/28 「浮動小数」という表記を、「浮動小数点数」に修正。
’2013/12/2 printf関数に %lf を指定していた箇所を %f に修正。
’2009/7/10 新規作成。
Programming Place Plus のトップページへ