lesson2(update:2020/10/24) [例 題2a例題2b課題2]


2.データ型と変数


[ prev | next | index ]

このページではデータを記録する形式を定めるデータ型と、そのデータを実際に記憶する場所である変数を紹介します。C言語ではデータ形式に機種(計算機の機種)や処理系(OSやC言語の処理系)依存の部分が残っています。

目次

 

[目次]


2.1 データの表現法

 計算機のメモリーは電荷の有無等の二値でデータを記憶します。二値データ1個の情報量を1bitと言い、現在8bitを1byteとする単 位 がメモリー容量でよく使われます。パソコンでも256Mbyte程度のメモリーを積んで います。(Mはメガ、100万の意味です)  1bitで2つの選択肢から1個を指定できます。2bitでは二値を0、1で現すと00,01,10,11の4種類の状態があり、1byte では256=28の状態が取れます。

文字:
 英語のアルファベット大文字、小文字、数字などは空白文字やコロン等の記号まで合わせても 100種類ほどですから1byteあれば文字を個々の状態に対応させる余裕が十分あります。文字と二値データの対応関係の決め方は色々有りますが、パソコ ンで良く使われるのが7bitのASCII コード表です。日本のJISコードC6220では半角カタカナを ASCIIコードに追加して1byte (8bit)のコードにしています。

数値:
 数値とbitコードの対応も色々なものが考えられますが、二進数を使えば1byteで0から255までの数値を 現せます。負の数を表わす場合は2の補数表示がよく用いられ1byteで−128から+127の数値を表わせます。

 bit数を2byte、4byte、 8byteと増やせば表現できる範囲は216、232、264と実用上は問題の無い大 きさになります。ところで、計算機の四則演算回路は有限のbit数の数値を前提に作られていますから、このbit数に合わせると計算が効率良く行えます。 従って、プログラム中での数値データの表し方は必要な範囲の数値が表せて、計算も高速に行える様に 機種依存することがあります。  例えば、昔の16ビットCPUを使うパソコンでは整数に16ビットつまり2バイトを割り当てていたのに、現在の32ビットCPUを使うパソコンはほとんどが整数に32ビットつまり4バイトを割り当てています。

[目次]


2.2 C言語のデータ型

 プログラミング言語ではデータの表現方法をデータ型(data type)として定めています。C言語では文字型charや 整数型intが代表的です。charは1byteでOS等に依存して文字コー ドは様々です。intも同様ですが計算機がもっとも効率的に計算できるbyte数に決められる場合が多いようです。最近のパソコンでは4byte、古いパ ソコンでは2byteの様な違いが有ります。

 (注)C言語は計算機の性能を最大限に引き出すことが重要な目的なのでデータ型の表現は一つに 定まっていません。

主なデータ型

データ型名
値の範囲
byte数
メモ

char/文字型

0〜255 or -128〜127(数と見た場合)

1byte

文字との対応は文字コード表に依存

short/短整数型

符号付き整数、範囲はbyte数依存

2byte

intよりも短い整数。intと同じ場合もある

int/整数型

符号付き整数、範囲はbyte数依存

2-4byte

2byteの整数では±3万程度の範囲しか表せない

long/長整数型

符号付き整数、範囲はbyte数依存

4-8byte

intよりも長い整数。intと同じ場合もある

float/実数型

処理系依存です

4byte

有効数字6桁程度 (符号、指数部、仮数部を持つ)

double/倍精度実数型

処理系依存です

8byte

有効数字16桁程度

long double/拡張精度....

doubleと同じかそれ以上の精度を持つ

8byte以上

doubleと同じ場合も多い

数値32-127に対応した文字の出力

文字は文字コード表で数値と対応付けされています以下のプログラムは数値32-127がどの文字と対応しているかを書き出すプログラムです。書式指定子%cで数値は 文字コード表の対応する文字として出力されます。

ASCIIコードは0-127の数値と対応する文字および制御コードを定めています。ここで、0-31と127は文字には対応しない制御コードです。そのため127のところは文字として表示されていません。また92に対応する文字が¥になっていますがこれは元のASCIIコードではバックスラッシュであるなどASCIIコードとは若干違う結果になっています。

#include<stdio.h>
int main(void)
{
	printf("32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 \n");
	printf("%c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c \n"
	       ,32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47
	);
	printf("48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62 ,63 \n");
	printf("%c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c \n"
	       ,48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63
	);
	printf("64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79 \n");
	printf("%c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c \n"
	       ,64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79
	);
	printf("80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95\n");
	printf("%c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c \n"
	       ,80,  81,  82 , 83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95
	);
	printf("96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111\n");
	printf("%c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c \n"
	       ,96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111
	);
	printf("112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127\n");
	printf("%c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c   %c \n"
	       ,112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127
	);
	
	return 0;
}
/*実行結果
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 
    !   "   #   $   %   &   '   (   )   *   +   ,   -   .   / 
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62 ,63 
0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ? 
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79 
@   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O 
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95
P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _ 
96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111
`   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o 
112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127
p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~ 

------------------------------------------
上のプログラムは実は次のように書くほうがコンパクトで間違いも少なくなる
#include<stdio.h>
int main(void)
{
	int s,c;
	for(s=32;s<127;s+=16){
		for(c=s;c<s+16;c++) printf("%4d",c);
		printf("\n");
		for(c=s;c<s+16;c++) printf("%4c",c);
		printf("\n");
	}
	return 0;
}
*/
 

[目次]


例題2a データ型の必要byte数

sizeof()演算子を用いて各データ型のバイト数を調べるプログラムを実行してみた。sizeofの使い方は

sizeof(型名or変数名)

でバイト単位のメモリ量が戻されます。

#include<stdio.h>

int main(void)
{
printf("sizeof(char)=%d\n",sizeof(char));
printf("sizeof(short)=%d\n",sizeof(short));
printf("sizeof(int)=%d\n",sizeof(int));
printf("sizeof(long)=%d\n",sizeof(long));
printf("sizeof(float)=%d\n",sizeof(float));
printf("sizeof(double)=%d\n",sizeof(double));
printf("sizeof(long double)=%d\n",sizeof(long double));
return 0;
}
 

ここで例題1と異なりprintfの引数は文字列とsizeof が戻す数値の2個あります。1つ目の引数「文字列」は書き出す文字列のフォーマットを指示します。この文字列中の%dは10進数への書式指定子で、2番目の引数の値が10進数の文字列に変換されて%dを置き換えて書きだす文字列が作られます。

printf( )での書式指定子

printfの( )の中で" "で囲まれた中の「%d」を書式指定子と言います。 書式指定子は、%英字で" "の直後の「,」の後ろに並ぶ変数の内容に置き換えて、画面にどう出力するかを指定する記号です。 主な書式指定には文字、10進整数、倍精度実数、文字列の%c, %d, %f, %s等 があります。フォーマット文字列中に複数の書式指定が可能で、その分だけ2番目以下の引数が増えることになります。例題1の様に書式指定が無ければフォーマット文字列がそのまま書き出されます。書式指定子の英字の部分は、変数のデータ型により異なり、下表のようになります。

書式指定子 データ型
%c char
%s 文字列へのポインタ
%hd short
%d int
%ld long
%f float, double

printf("c=%c i=%d d=%f", 'A', 10, 3.1415);は「c=A i=10 d=3.141500」を書き出します。書式指定子と出力する変数との個数は、一致していなければなりません。


OS:WindowsXP上のGNUのC言語環境(cygwin)でコンパイルと実行を行ってbyte数を調べた結果。

D:\cygwin>gcc page2.c  GNUのCコンパイラで上記内容のソースファイルpage2.cをコンパイル&りンク
 
D:\cygwin>a.exe 実行可能プログラムa.exeを実行
sizeof(char)=1
sizeof(short)=2
sizeof(int)=4 この環境ではintが4byteなのがわかる
sizeof(long)=4 longがintと同じですね。ちょっと残念
sizeof(float)=4
sizeof(double)=8
sizeof(long double)=12 long doubleはdoubleより長いですね。うれしい?

データ型の細かい指定を行うこともできます。データ型のところで使ったshortlongも元はshort int、long intと書くべきところを、intを省略した形です。次に説明するunsignedsignedは数値との対応を0以上の 整数とするか、正負の範囲の整数とするかを指定します。

符号無し整数型(unsigned integer type)

2byteの整数型では32767は二進数の表現で0111 1111 1111 1111となる。+1すると桁上がりが起きて1000 0000 0000 0000となる。これを普通に二進数と見れば32768となる。ここで、2byteで現せる最大の数値1111 1111 1111 1111に+1する場合を考えると、17桁目に桁上がりが起きるが17桁めは2byte16桁を越えているために下の16桁のみがメモリーに残ると考えれ ば値は0000 0000 0000 0000で数値0と同じになる。2の補数表示は+1して0になる1111 1111 1111 1111を−1に対応させる。以下に符号無し整数型と符号付き整数型の数値との対応を示す。2の補数表示では一番上位のbitが1の場合は負の数である。

2byte(16bit)のデータ

符号無し整数型での対応
unsigned

符号付き整数型での対応
signed

0000 0000 0000 0000

0

0

0000 0000 0000 0001

1

1

+1を続けて行く
...
...

0111 1111 1111 1111

32767

32767

1000 0000 0000 0000

32768

ここで値が跳ぶ -32768

1000 0000 0000 0001

32769

-32767

+1を続けて行く
...
...

1111 1111 1111 1111

65535

-1

(1)0000 0000 0000 0000

ここで値が跳ぶ   0

0

0000 0000 0000 0001

1

1

データ型の名前で前にunsignedを付けると符号無しの型にsignedを付けると符号付きの型になる。short、int、longはデ フォルトで符号付きなのでsignedを前に付ける必要は無い。

(注)char型については符号付きの有無は決められていないので言語環境に依存する。従って char型を数値として使いたい場合はsigned、unsignedを付けて明確にする必要が有る

[目次]


2.3 データの保存

メモリーの番地

 データは前述のデータ型の形式でメモリに記憶されます。メモリーは下記のように8ビットを1個のまとまりとして番地(address)が割り当てられています。番地の値は64Mbyteのメモリーがあれば 0番地から64M番地までです。たとえば、32767番地からの2byteにshort int型のデータ  -2(1111 1111 1111 1110)が書き込まれているならメモリの内容は下記の様になります。ここで、2バイトを32767と32768番地に格納する順番が問題ですが、これは 計算機のCPUに依存します。(下記の注ビッグ・エンディアンとリトル・エンディアン)

番地

8bitのデータ

0

???? ????

1

???? ????

....
....

32767

下位8桁 
1111 1110

32768

上位8桁
1111 1111

....
....
注:「ビッグ・エンディアンとリトル・エンディアン」
複数バイトのデータの下位バイトを下位番地に割り当てる方式をリトル・エンディアンといい、演習用のパソコンに使われている Intel系のプロセッサはこの方式を前提に演算を行う。ビッグ・エンディアンは逆の割り当て方式。
エンディアン図

変数名と番地

 機械語の命令ではXX番地のデータを読み出せとか、計算結果をYY番地に書き込めとか、データは番地を指定して操作されます。従って、プログラ ムではデータを入れるメモリ番地をしっかりと決めておく必要が有ります。しかし、データを書き込む 番地が重ならない様に決めていくのはプログラマーにとって煩わしい作業です。。

 プログラミング言語では多くの場合、メモリ番地を考える必要が無いように、 ソースプログラム中ではデータの記憶場所として変数(variable)を使い、記憶場所は変数名で区別され ています。変数名とメモリ番地 の対応付けは言語処理系が行うので、プログラマーが番地を決める必要はありません。

[目次]


2.4 C言語の変数

 C言語ではデータ型を指定して変数を宣言します。型が指定されていればデータを表すbitパターンが決まります。さらに、データを格納するのに必要なメモリ量 も決まります。 この他にも、変数の型が指定されているとコンパイルの段階で型チェックを行ってプログラムミスを減らせます。次に説明する四則演算の様な場合にはデー タの型に応じた処理を自動的に選択するといったことにも使われます。

メモ:
 メモリー上のビットのパターンが何を意味するかはパターンを見ただけでは皆目分かりません。しかし、プログラムはデータ型を知っているのでパターンを適切に解釈して利用することができるのです。

C言語での変数の宣言方法は次の様になります。

char c;
int i;
double pi=3.1415;

型名の後に定義する変数名を書きます「=」を用いて初期値を指定することも可能です。変数名は英字or下線(アンダースコア)で始まる名前でC の予約語(reserved word)と重複してはいけません。名前といいましたがより正確な表現は識別子(identifier)です。識別子は区別できて重複しないものですから、同じ名前で型が違う変数を作ることはできません。 intとかdoubleといった名前も型名と重複するので使えません。基本的な型名はC言語の予約語に含まれています。

(注)識別子の有効先頭文字数は言語処理系依存です。外部結合で先頭から6文字以上、内部結合で先頭から31文字以上がANSI Cの規格で定 められています。外部結合では大文字小文字の区別は処理系依存ですが、内部結合では大文字小文字を区別します。プログラムを作成するとき先頭から31文字目までが同じ名前は避けた方が安全です。ライブラリの様なも のを作る場合は外部結合についても考慮が必要になるでしょう。

[目次]


例題2b char型変数

実際にchar型の変数cを用いて自分が使っている環境での文字コード、符号付きかどうか等を調べましょう。

#include <stdio.h>

int main(void )
{
	char c=100;/*文字型*/
	unsigned char uc=100;/*符号なし文字型*/
	signed char sc=100;/*符号付き文字型*/

	printf("文字として書き出す c='%c' uc='%c' sc='%c'\n",c,uc,sc);
	printf("数値として書き出す c=%d uc=%d sc=%d\n",c,uc,sc);

	printf("c,uc,scに100を加えて\n");
	c=c+100; uc=uc+100; sc=sc+100;

	printf("文字として書き出す c='%c' uc='%c' sc='%c'\n",c,uc,sc);
	printf("数値として書き出す c=%d uc=%d sc=%d\n",c,uc,sc);

	return 0;
}

例題2aと比べると変数の宣言、/*コメント*/、c=c+100の様な計算がある点が異なります。

OS:WindowsXP上のMicrosoftC/C++でコンパイルと実行を行ってchar型がsignedかunsignedか調べてみた。
プログラム2bの実行結果
文字として書き出す c='d' uc='d' sc='d' 数値として書き出す c=100 uc=100 sc=100 c,uc,scに100を加えて 文字として書き出す c='ネ' uc='ネ' sc='ネ' 数値として書き出す c=-56 uc=200 sc=-56


2.5 演習課題

課題2 定数の表示  

このファイル(p02.c)を右クリック「対象をファイルに保存」で保存し、このファイルを使ってプログラムを作成せよ。

以下のように変数に定数を代入し、出力結果が以下のようになるプログラムを完成させよ。

変数 a: 10進数定数 50を代入
変数 b: 16進数定数 C9を代入
変数 c: 8進数定数 33を代入
変数 d: 倍精度実数 0.07を代入
変数 e: 文字定数 Bを代入

ヒント:

1) 赤線の部分は、sizeof()演算子を使いましょう。

2) 変数に定数を代入するには、下の表に従います。

定数の種類 表記方法 表記例
整数定数 8進数 先頭に0(ゼロ)を付ける 045
10進数 通常の10進数と同じ 350
16進数 先頭に0xまたは0Xを付ける 0x9B (または、0X9B)
浮動小数点定数 小数点形式はピリオド(.)を付ける 2.72
指数形式は指数文字(eまたはE)を付ける 1.56e-8
文字定数 ' 'で囲む 'A'
文字列定数 " "で囲む "ABCDEFG"

3) printf( )での書式指定子

書式指定子 データ型
%d int(符号付き10進数)
%u int(符号なし10進数)
%x int(16進数)
%o (オー) int(8進数)
%f double
%c char

課題2が終了し、余裕のある人は、次の問題もやってみよう。 補足問題

[目次]


[ prev | next | index ]