lesson2(update:2020/10/24) [例 題2a|例題2b|課題2]
このページではデータを記録する形式を定めるデータ型と、そのデータを実際に記憶する場所である変数を紹介します。C言語ではデータ形式に機種(計算機の機種)や処理系(OSやC言語の処理系)依存の部分が残っています。
目次
[目次]
計算機のメモリーは電荷の有無等の二値でデータを記憶します。二値データ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バイトを割り当てています。
[目次]
プログラミング言語ではデータの表現方法をデータ型(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より長いですね。うれしい?
データ型の細かい指定を行うこともできます。データ型のところで使ったshortやlongも元はshort int、long intと書くべきところを、intを省略した形です。次に説明するunsignedとsignedは数値との対応を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符号付き整数型での対応
signed0000 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を付けて明確にする必要が有る。
[目次]
メモリーの番地
データは前述のデータ型の形式でメモリに記憶されます。メモリーは下記のように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 111032768
上位8桁
1111 1111.... ....
注:「ビッグ・エンディアンとリトル・エンディアン」
複数バイトのデータの下位バイトを下位番地に割り当てる方式をリトル・エンディアンといい、演習用のパソコンに使われている Intel系のプロセッサはこの方式を前提に演算を行う。ビッグ・エンディアンは逆の割り当て方式。
変数名と番地
機械語の命令ではXX番地のデータを読み出せとか、計算結果をYY番地に書き込めとか、データは番地を指定して操作されます。従って、プログラ ムではデータを入れるメモリ番地をしっかりと決めておく必要が有ります。しかし、データを書き込む 番地が重ならない様に決めていくのはプログラマーにとって煩わしい作業です。。
プログラミング言語では多くの場合、メモリ番地を考える必要が無いように、 ソースプログラム中ではデータの記憶場所として変数(variable)を使い、記憶場所は変数名で区別され ています。変数名とメモリ番地 の対応付けは言語処理系が行うので、プログラマーが番地を決める必要はありません。
[目次]
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の様な計算がある点が異なります。
- 変数の宣言で文字型の変数cと符合無し、符号付の文字型の変数uc,scをそれぞれ用意しました。同じ値100を入れておきます。
- /* と */で囲まれた部分はコメントと呼ばれるものでソースプログラムのコンパイルに際して無視されます。
注釈などをプログラム中に書いておくのに使います。- printf文の書式文字列("文字として書き出す c='%c' uc='%c' sc='%c'\n")の部分で%cは 文字コード表に従ってc,uc,scなどの値に対応した文字に置き換える指示です。さらに、%dは10進数の文字列で置き換えます。
- c=c+100で「=」は等号ではありません。代入演算子です。
この1行の意味は「cの値に100加えた結果をcに代入する」です。従ってunsignedならcは200になります。でもsignedだと?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
- はじめにそのまま文字として出力すると3つの変数の値は同じ「d」となる。10進数で出すとこれも100で同じ。
- ここで+100すると符合無しなら200になるが、符号付だと-56になるはず。
- 文字として出力すると同じ「ネ」となるが10進数として出すとuc=200、c=sc=-56と異なる結果になった。
これで、この環境ではcharが符号付なのがわかった。- 文字コード表が日本語用に拡張されたものなのでuc=200に対応する文字は半角の「ネ」、c,scもメモリに記憶されたビットパターンはucと同じなので、対応する文字も同じ。
- 10進数として出力する場合のみ違いが出ている。足し算で100増やしたのに負の数になるなんて少し変?
これは整数を有限のbit数で表した為で、計算機の世界では当たり前!! 慣れて下さい
※コンパイラはprintfにデータを渡すとき、int型以下の整数型(charやshortなど)の値はint型に変換してから渡します。-コンパイラはc,uc,scが同じビットパターンであっても、ucが符合無しであることを知っているので、ucはint型の200に、c,scは符号付なのでint型の-56に変換してprintfに渡す プログラムに翻訳します。
・このファイル(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が終了し、余裕のある人は、次の問題もやってみよう。 補足問題
[目次]