応用理工学情報処理 6回目

(a) 基本型(1) 整数と文字型 

今回は、テキスト第7章、基本形を学習します。ここでは以下のようなポイントがあります。

(1)基本型(int, double, char など)、型指定子(signed, unsigned)、ビット単位の演算子

(2)16進数 255 => 0xFF, printf(“%X”, 0xFF); 16進数は、0 1 2 3 4 5 6 7 8 9 A B C D E Fで数字を表す。

(3)整数型の最大値 limits.hヘッダーファイルで定義されている。 #define UCHAR_MAX 255U

「1」整数型の範囲を表示してみましょう。(テキストp.177) 以下のプログラムを実行してみてください。

整数型は扱っている数値の正確(厳密)な表現である反面、表現できる数値の上限、下限が存在します。double型(倍精度浮動小数点型)の方がはるかに広い範囲の数値を表現出来ますが、有効桁数が存在します。

/*
	整数型の表現範囲を表示する
*/

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

int main(void)
{
    printf("char          : %d~%d\n",	 CHAR_MIN,	CHAR_MAX);
    printf("signed char   : %d~%d\n",	 SCHAR_MIN,	SCHAR_MAX);
    printf("unsigned char : %d~%d\n",	 0,	UCHAR_MAX);

    printf("short         : %d~%d\n",	 SHRT_MIN,	SHRT_MAX);
    printf("int           : %d~%d\n",	 INT_MIN,	INT_MAX);
    printf("long          : %ld~%ld\n", LONG_MIN,	LONG_MAX);

    printf("unsigned short: %d~%d\n",	 0,	USHRT_MAX);
    printf("unsigned      : %d~%u\n",	 0,	UINT_MAX);
    printf("unsigned long : %ld~%lu\n",0,	ULONG_MAX);

    return (0);
}

int型は符号付き整数であり、unsigned int (unsignedと省略可)は符号なし整数です。符号付き整数では最上位ビットが1の場合負の数を表します。符号付き整数の出力変換指定は%dであり、これまでprintf関数等で用いてきました。これに対応する符号なし整数の変換指定は%uです。符号付きと符号なし整数では、それぞれ表現できる数値の範囲が異なります。それを超えると正しい計算が出来なくなります。次のプログラムはそれぞれの最大値に1を加えた場合にどのようになるかを確認するプログラムです。

/*
	符号付きと符号なし整数型の最大値に1を加えた結果を確認する
*/

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

int main(void)
{
    printf("整数型の最大値(intとして表示)                :%d\n", INT_MAX);
    printf("整数型の最大値 + 1(intとして表示)            :%d\n", INT_MAX + 1);
    printf("整数型の最大値(unsignedとして表示)           :%u\n", INT_MAX);
    printf("整数型の最大値 + 1(unsignedとして表示)       :%u\n", INT_MAX + 1);
    printf("符号なし整数型の最大値(intとして表示)         :%d\n", UINT_MAX);
    printf("符号なし整数型の最大値 + 1(intとして表示)     :%d\n", UINT_MAX + 1);
    printf("符号なし整数型の最大値(unsignedとして表示)    :%u\n", UINT_MAX);
    printf("符号なし整数型の最大値 + 1(unsignedとして表示):%u\n", UINT_MAX + 1);

    return (0);
}

(4)sizeof演算子 sizeof(型名)=>型名のバイト数(charの何倍か)

「2」sizeof演算子は、もう少し複雑なプログラムを作成するようになるとバイナリファイルの読み書きや配列の動的確保に良く使用します。ここでは基本型のサイズを表示してみましょう。(テキストp.180, List7-3改)

/*
	型の大きさを表示する
*/

#include  <stdio.h>

int main(void)
{
	printf("sizeof(char)  = %u\n", (unsigned)sizeof(char));
	printf("sizeof(short) = %u\n", (unsigned)sizeof(short));
	printf("sizeof(int)   = %u\n", (unsigned)sizeof(int));
	printf("sizeof(long)  = %u\n", (unsigned)sizeof(long));
	printf("sizeof(float)  = %u\n", (unsigned)sizeof(float));
	printf("sizeof(double)  = %u\n", (unsigned)sizeof(double));
	printf("sizeof(long double)  = %u\n", (unsigned)sizeof(long double));

	return (0);
}

(5)typedef宣言、ある型名(intやunsignedなど)に別の名前を与える。 (例)typedef unsigned size_t;
typedef宣言は、もう少し後に出てくる構造体宣言に簡潔な名前を与えるときに良く使います。sizeof演算子から返される値は、上の例のように定義されたsize_t型です。

====== 演習問題 6A (p6a.c) ==============

演習問題3Bでは、2重forループを用いて九九の表を作成しました。ここでは16進数表示によるこれに対応する表(FFの表、前の課題での9×9=といった部分は除く)を以下の通り(Webブラウザでうまく文字幅を合わせられなくてここでは上下でずれていますが、プログラムの出力は上下そろうように出力)に出力するプログラムを作成して下さい。

<出力例>
 1   2  3  4   5  6   7  8  9   A  B  C  D   E   F 
 2   4  6  8  A  C  E 10 12 14 16 18 1A 1C 1E 
 3   6  9  C  F 12 15 18 1B 1E 21 24 27 2A 2D 
 4   8  C 10 14 18 1C 20 24 28 2C 30 34 38 3C 
 5   A  F 14 19 1E 23 28 2D 32 37 3C 41 46 4B 
 6   C 12 18 1E 24 2A 30 36 3C 42 48 4E 54 5A 
 7   E 15 1C 23 2A 31 38 3F 46 4D 54 5B 62 69 
 8 10 18 20 28 30 38 40 48 50 58 60 68 70 78 
 9 12 1B 24 2D 36 3F 48 51 5A 63 6C 75 7E 87 
 A 14 1E 28 32 3C 46 50 5A 64 6E 78 82 8C 96 
 B 16 21 2C 37 42 4D 58 63 6E 79 84 8F 9A A5 
 C 18 24 30 3C 48 54 60 6C 78 84 90 9C A8 B4 
 D 1A 27 34 41 4E 5B 68 75 82 8F 9C A9 B6 C3 
 E 1C 2A 38 46 54 62 70 7E 8C 9A A8 B6 C4 D2 
 F 1E 2D 3C 4B 5A 69 78 87 96 A5 B4 C3 D2 E1 

(b) 基本型(2) bit演算, math.h

(6)整数接尾語 1=> int, 1L=> long, 1u=> unsigned int

(7)整数の2の補数表現=> 1 - 1 = 0 => 00000001 + 11111111 = 100000000

(8)ビット単位のAND, OR, XOR, NOT => &, |, ^, ~

(9)ビット単位のシフト演算子 <<, >>

「3」整数の内部のビットパターンを表示してみましょう。

以下の例では、表示したいビットをシフト演算子>>で最下位ビットに移動した後、1とのビット単位のANDを見ることによってそれが1か0かを判断しています。

#include <stdio.h>

int main (void)
{
	int i, x;
	
	printf("整数を入力してください:"); scanf("%d", &x);
	for (i = 8*sizeof(int) - 1; i >= 0; i--){
		putchar(((x >> i) & 1U) ? '1' : '0');
		if (!(i%8)) printf(" ");
	}
	printf("\n");
	
	return 0;
}

「4」ビット単位の論理演算子の動作確認(AND, OR, XOR, NOT)(テキストp.189, List7-6)

/*
	unsigned型の論理積・論理和・排他的論理和・1の補数を表示
*/

#include  <stdio.h>

/*--- 整数x中のセットされたビット数を返す ---*/
int count_bits(unsigned x)
{
	int	 count = 0;
	while (x) {
		if (x & 1U) count++;
		x >>= 1;
	}
	return (count);
}

/*--- unsigned型のビット数を返す ---*/
int int_bits(void)
{
	return (count_bits(~0U));
}

/*--- unsigned型のビット内容を表示 ---*/
void print_bits(unsigned x)
{
	int	 i;
	for (i = int_bits() - 1; i >= 0; i--)
		putchar(((x >> i) & 1U) ? '1' : '0');
}

int main(void)
{
	unsigned  na, nb;

	puts("二つの非負の整数を入力してください。");
	printf("整数A:");	  scanf("%u", &na);
	printf("整数B:");	  scanf("%u", &nb);

	printf("\nA<<2     = ");   print_bits(na<<2);
	printf("\nB>>2     = ");   print_bits(nb>>2);
	printf("\nA & B = ");   print_bits(na & nb);		/* 論理積 */
	printf("\nA | B = ");   print_bits(na | nb);		/* 論理和 */
	printf("\nA ^ B = ");   print_bits(na ^ nb);		/* 排他的論理和 */
	printf("\n~A    = ");   print_bits(~na);			/* 1の補数 */
	printf("\n~B    = ");   print_bits(~nb);			/* 1の補数 */
	putchar('\n');

	return (0);
}
ビット単位の演算は、外部機器を制御するプログラムを書くときなどハードウェアに関係する部分で良く使います。しっかり理解しましょう。 

(10)浮動小数点
float 32ビット 符号1、指数部7(-37~38)、小数部24(6桁)
double 64ビット 符号1、指数部10(-307~308)、小数部53(15桁)

(11)浮動小数点接尾語 1.0 => double, 1.0F => float, 1.0L => long double

(12)数学関数 #include <math.h> でmath.hファイルをインクルードしておくこと。
sqrt(double x), sin(double x), cos(double x), tan(double x), atan(double x), exp(double x), log(double x), pow(double x, double y) 全て返り値はdouble型

====== 演習問題 6B (p6b.c) ==============

2つの整数a, bをキーボードから入力して、a + b =cを計算する場合の それぞれのビットパターンを下記出力例のとおりに表示するプログラムを作成せよ。 ただし、イタリック体太字の数字はキーボードから入力した数値を表す。

<出力例>
2つの整数a, bを入力して、a + b = cの計算のビットパターンを表示します。
a = 3 b = -1
c = 2 aのビットパターン: 00000000 00000000 00000000 00000011 bのビットパターン: 11111111 11111111 11111111 11111111 cのビットパターン: 00000000 00000000 00000000 00000010
(ヒント)整数を引数として受け取ってそのビットパターンを標準出力に表示する関数は以下のように記述できる。
void print_int_bits(unsigned int n)
{
	int nbit = 8 * sizeof(int);
	unsigned int m;
	m = 1 << nbit-1;
	for (int i = 1; i <= nbit; i++){
		printf("%d", (n & m) ? 1 : 0);
		if (!(i % 8)) printf(" ");
		m >>= 1;
	}
	printf("\n");
}

====== 演習問題 6C (p6c.c) ==============

ビット単位の排他的論理和( ^ )は対応するビットが同じもの同志:例えば1^1ならば0, 違うもの同志:例えば1^0なら1となる論理演算子である。キーボードから2つの整数xとposを入力し、xのpos番目の位置のビットを反転した値を下記出力例に示すとおりに出力するプログラムをこの^演算子を用いて作成せよ。ビット位置は最下位ビットを0番目とする。例えばxとして0を入力して反転するビット位置posを10とすると、この値は2の10乗で1024となる。入力された数値が適当でない場合の対応は行わなくて良い。ここで、下記出力例において、(0~31)の31の値は8*sizeof(int)-1で計算される値である。(ヒント:整数 1 は最下位のビットのみ 1 である。これを指定の位置までシフト演算子 << で移動して、その値との排他的論理和( ^ )を計算する。そのようにすると、そこのビットが0であれば1に、1であれば0となり、ビットが反転する。1をシフトした数はその位置以外は0なので、排他的論理和によってそれ以外の場所の値は変化しない。)

<出力例(太字イタリック体の数値はキーボードから入力した値を示す)>
整数を入力: 0
反転するビットの位置を入力(0~31): 10
反転した結果: 1024