最近のトラックバック

2.2 文字コード(ASCIIコード)表

2.2     文字コード(ASCIIコード)表

00 NULL

01 SOH

02 STX

03 ETX

04 EOT

05 ENQ

06 ACK

07 BEL

08 BS

09 HT

0A LF

0B VT

0C FF

0D CR

0E SO

0F SI

10 DLE

11 DC1

12 DC2

13 DC3

14 DC4

15 NAK

16 SYN

17 ETB

18 CAN

19 EM 

1A SUB

1B ESC

1C FS 

1D GS 

1E RS 

1F VS 

20   

21 !   

22 "   

23 #   

24 $   

25 %   

26 &   

27 '   

28 (   

29 )   

2A *   

2B +   

2C ,   

2D -   

2E .   

2F /   

30 0   

31 1   

32 2   

33 3   

34 4   

35 5   

36 6   

37 7   

38 8   

39 9   

3A :   

3B ;   

3C <   

3D =   

3E >   

3F ?   

40 @   

41 A   

42 B   

43 C   

44 D   

45 E   

46 F   

47 G   

48 H   

49 I   

4A J   

4B K   

4C L   

4D M   

4E N   

4F O   

50 P   

51 Q   

52 R   

53 S   

54 T  

55 U   

56 V   

57 W   

58 X   

59 Y   

5A Z   

5B [   

5C \   

5D ]   

5E ^   

5F _   

60 `   

61 a   

62 b   

63 c   

64 d   

65 e   

66 f   

67 g   

68 h   

69 i   

6A j   

6B k   

6C l   

6D m   

6E n   

6F o   

70 p   

71 q   

72 r   

73 s   

74 t   

75 u   

76 v   

77 w   

78 x   

79 y   

7A z   

7B {   

7C |   

7D }   

7E ~   

7F DEL

このアスキー表あるいはキャラクターコード表はコンピュータのROM(Read Only Memory)に格納されているのであって、たとえこれらの格納されているアドレスを見つけ出して書き換えようとしても書き換えることは出来ません。

先頭に\0に値するNULLがあります。0Dには\n改行に相当するLF(ラインフィールド)があります。と言うことは標準関数のprintf(\n);の代わりに16進数でprintf(\x0A;と記述しても同じ動作をするわけです。あまり8進数を利用することは少なくなりましたが、もし改行を8進数で記述するならば、関数電卓を使って12を取得しprintf(\012);と記述しても同じ動作をします。この頭の0は8進数を表す時に使用するものです。

【 問題 10 】

1.     ブザーを鳴らすのはアラームの意味のprintf(\a);です。では16進数と8進数で表すにはどうしたらいいでしょうか。

2.     バックスペースを表すのはBack Spaceの頭文字を取ったprintf(\b);です。では16進数と8進数であらわすにはどうしたらよいでしょうか。

(ヒント)

1.     ブザーはキャラクターコードのBELに当たります。

2.     バックスペースはキャラクターコードのBSに当たります。

キャラクターコードを出力するプログラムコードの例を掲載します。参考程度でかまいません。いろいろな書き方がありますが、この参考書を終了後、これだけ書けるようになっていれば初心者としては十分なレベルに達しているのではないかと思います。

         /*************************

    アスキーコード表

**************************/

#include <stdio.h>

  /***  プロトタイプ宣言    ****/

void DrawLine( int x );

void main()

{

     /**** 変数の宣言と初期化 ****/

   int  i = 0, j = 0;     // int型変数i,jを宣言して0を代入して初期化

     /**** 構造体 ****/

    struct ascii_code

   {

         char name[5];      // 文字列の最後に'\0'を入れるため1つ多く添え字を宣言

   };

     /**** 構造体 ****/

   static struct ascii_code ascii_name[] =

   {

         { "NULL"}, { "SOH" }, { "STX" }, { "ETX" },

         { "EOT" }, { "ENQ" }, { "ACK" }, { "BEL" },

         { "BS"  }, { "HT"  }, { "LF"  }, { "VT"  },

         { "FF"  }, { "CR"  }, { "SO"  }, { "SI"  },

         { "DLE" }, { "DC1" }, { "DC2" }, { "DC3" },

         { "DC4" }, { "NAK" }, { "SYN" }, { "ETB" },

         { "CAN" }, { "EM"  }, { "SUB" }, { "ESC" },

         { "FS"  }, { "GS"  }, { "RS"  }, { "VS"  },

         { "DEL" },

   };

   

     /****    キャラクターコード0~127まで繰り返す ****/

   for ( i = 0 ; i <= 127 ; i++ )

   {

           /**** 0の場合 ****/

         if(i == 0)

         {

                     DrawLine(65);    // ハイフンを65個並べる

                     printf("|");     // 区切りを入れる

         }

          /**** 0より大きく8で割り切れる場合 ****/

         else if( (i % 8 == 0 ) && ( i > 0 ) )

         {

                     printf("\n");    // 改行する

                     DrawLine(65);    // ハイフンを65個並べる

                     printf("|");     // 区切りを入れる

         }

          /**** 16より小さく127でない場合 ****/

         if( ( i < 16) && (i != 127) )

         {

                       // 16進数とキャラクタ(左寄せ)で構造体のメンバ変数を出力

                     printf("0%X %-4s|", i , ascii_name[i].name);

         }

          /**** 16以上で32より小さくなおかつ127でない場合 ****/

         else if( (i >=16) && ( i < 32) && (i != 127) )

         {

                       // 16進数とキャラクタ(左寄せ)で構造体のメンバ変数を出力

                     printf("%2X %-4s|", i , ascii_name[i].name);

         }

         /**** 127の場合 ****/

         else if( i == 127 )

         {

               // 16進数とキャラクタ(左寄せ)で構造体のメンバ変数を出力      

                     printf("%2X %-4s|", i , ascii_name[32].name);

         }

         /**** 上記以外の場合 ****/

         else

         {

                       // 16真数とキャラクタ(左寄せ)でアスキーコードを出力

                     printf("%2X %-4c|", i , i );

         }                     

   }

   printf("\n");           // 改行する

   DrawLine(64);           // 64個分ハイフンを並べる

}

/**************************************************

  区切り用ハイフン出力関数

   機能   : 引数分のハイフンを出力後、改行する

   引数 x: ハイフンの数

   戻り値:なし

**************************************************/

void DrawLine( int x )

{

    int j = 0;

   for( j = 0; j < x ; j++ )

   {

         printf("-");

   }

   printf("\n");

}

2.2 データ型

2.2     データ型

人間は 1+1=2, 1/10 = 0.1, 1-40000 = -39999 と整数、小数、負の数であっても計算する前に特別な切り替えもせずに無理なく処理してしまう能力があります。しかしながらコンピュータはそういうわけにいかないのです。整数なのか、小数なのか、負の数なのか、どれだけの範囲が必要な数字なのか、どれだけの精度が必要なのかを計算する前に、変数に適切な型を宣言しておかなければならないのです。近年、高速なコンピュータ本体の低価格化とメモリーの下落に伴い、データの型の宣言もかなりいい加減になっている傾向があります。変数の範囲が0~255までの整数しか使わない場合は、1バイトしかメモリーを使う必要がありません。にもかかわらず最大の範囲である4バイトや8バイトの変数で宣言しているものがあります。確かにエラーも警告も発生はしません。また変数の宣言だけしておきながら、一切プログラムコードで使われない変数も多々見受けられます。勿論、使われなかったとしてもコンパイルエラーにはなりませんが、宣言した以上メモリは消費されます。こういった一度も使われなかった変数があるとコンパイル時に警告が出されているはずですが、それでも放置されているのを多々見受けられます。こういった状態にすることを「散らかす」といいます。まず制御系のマイコンのプログラムでは致命的になりかねません。また大きなシステムでもいつかこういったことを続けていると必ず大きな問題を引き起こす引き金になりかねません。まずは、コンパイル時に型の警告がでたら、使用していない変数をすべて削除することが大事です。削除すると行数が減り、結構プログラムコードが読みやすくなること気づきます。

変数名として半角の英数字(アルファベット大文字・小文字を含む)とアンダーバーを組み合わせて使うことが出来ます。ただし半角の数字だけとか半角の特殊文字の使用やたとえ英数字の組み合わせであっても12aというように数字で始まる変数名は許可されていません。もしaと12を組み合わせたいのであればa12、A12、a_12、A_12というふうに宣言します。

ではABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890というような長い変数名はどうでしょうか。確かに誤りではありません。コンパイルしてしまえば、その変数は1バイトから最大8バイトの型になってしまいます。さてどこに問題があるかと言いますと、コンパイルするコンピュータのメモリ量が足りないとコンパイルができなくなる恐れがあります。それから、業務で作成しているプログラムは数十MB、数百MB、中には1GBを越えるものもあります。これらのプログラムをコンパイルを始めて終了するまで30分以上待たなければならないというのは作業の中では日常的な光景です。文字列が長ければファイルの大きさが大きくなりいくらメモリ搭載量の多いコンピュータでも読み込みに時間がかかってしまいますので、短くてわかりやすい変数名にします。例をあげるならば、postal_code , address_1, guestname , TelephoneNumberなどです。規則はないのですが関数の中ではアルファベットの小文字を使用するのが大半を占めてはいます。近年一部大文字と小文字を織り交ぜて読みやすくしている宣言も増加の傾向にあります。理由は時代をさかのぼりますが、歴史のあるUNIXが最初から大文字と小文字を別々に扱っていたのに対しマイクロソフトの開発環境では当初アルファベットの大文字だけ(例 TELEPHONE)と小文字(例 telephone)だけを同一の文字列と扱ってきた歴史があるからです。ただしENSHURITSUというふうにすべて大文字だけで宣言するのは、プリプロセッサで使用するのが通例になっています。(例 #define ENSHURITSU 3.14)

bool型  (1バイト)

bool型と言うのは1もしくは0しか持ちません。一般に関数が真(true)か偽(false)かを判断するときや、条件式で条件を満たしたか満たさなかったかの判断結果を1か0で表すのが一般的です。Cの標準のコンパイラでは bool a = true; や bool a = false; と記述してコンパイルしてもエラーしますが、C++でコンパイラでCのプログラムをコンパイルするとコンパイルできるためtrueやfalseも使用できるものだいう誤った錯覚に陥りがちです。あくまでも bool a = 0; もしくは bool a = 1;と宣言するのが正しい表記です。

命令の一番小さい単位は8ビットの1バイトですから、かといってbool型が256種類使えるわけではないのです。確かにbool型は1バイトの宣言ですが、boolと宣言した段階で、1バイトの1ビット目だけを使用するという意味なのです。だから0と1しか持たないわけです。

試しにbool a= 2;と宣言するとどうなるかというと、予想に反してコンパイルエラーが出ずに、警告が出ます。出力するとaの値は1となります。つまり強制的に2という値を1に置き換えられたわけです。

bool 型  1バイト                             1ビット目のみ使用                

0

0

0

0

0

0

0

1

実際に宣言されたaのメモリサイズを調べるにはどうしたらよいでしょうか。それには標準関数の一つであるsizeof()を利用します。これはとても便利な関数で、後に学習する構造体で頻繁に使用することになります。

まず bool a = 0;と宣言した後、printf(aのサイズは %dです。\n, sizeof(a));と出力すると変数aが1バイトの型であることがわかります。

今回は bool a = 0; と宣言していますが、最初に bool a;と宣言した後で a = 0;と変数aに0を代入してもかまいません。全く同じ意味になります。

マシン語というのは同じアドレスにデータが上書きされない限り変更されないので、まずこれから使用する型の任意のデータを代入して準備しておきます。ここではa = 0;にあたります。こういった作業を「初期化」と言いますが、もしこの初期化をしなかった場合、プログラム中に意味不明の文字が表れ、つまり以前のデータが残っている状態になりプログラム全体に大きな問題を引き起こすことがあります。

----------------------------------------------------------------------

bool型              lesson13.c

----------------------------------------------------------------------

/**************************

    bool型

***************************/

#include <stdio.h>

void main()

{

           bool a = 0;               // bool型の変数aに0を代入し初期化する

           printf("a = %d \n", a );     // 変数aの値を出力する

           printf("aのサイズは%dバイトです。\n",sizeof(a)); // 変数aのサイズの出力

           a = 1;                    // aに1を代入する。

           printf("a = %d \n", a );     // 変数aの値を出力する

           return;

}

-------------------------------------------------------------------

実行結果

-------------------------------------------------------------------

a = 0

aのサイズは1バイトです。

a = 1

-------------------------------------------------------------------

【 問題6 】

1.       lesson13.cのbool a = 0; をbool a;と宣言する行とa = 0;と代入する行に分けて記述して結果が同じになることを確認しなさい。

2.       bool a=0;をbool a;と初期化を省略するとどのような結果になるか確認しなさい。

3.       lesson13.cのbool a =1; を bool a =2;へ変更し結果を確認しなさい。

(ヒント)

1.   型の宣言と変数の初期化を1行で行うと1行分短くなりコードが見やすくなりますが、論理的にみれば宣言と初期化を分けて記述する方がわかりやすい。

2.   意味不明の文字が表示されます。boolと宣言しているにもかかわらず、0か1以外の数字が表れます。ただし結果は開発環境に依存するので相違がある。

3.   結果は開発環境に依存するので相違がある。

char型  (1バイト)

char型と言うのはcharacterの略で1バイトの文字型です。文字型というと語弊があるのでここで誤解が生じないように説明します。1バイトを全部組み合わせても256通りしかできないので日本語は使えず、使えるのはキーボードに表示されている英数字と特殊文字だけです。アメリカで開発されたコンピュータはそれで十分だったわけですが、そのおかげで日本ではコンピュータの普及が遅れてしまいました。理由は至って簡単で日本語の問題です。パソコンより安価で日本語が使えるWord Processor(普及してすぐにワープロと言う言葉に短縮され浸透した)がオフィスのペンにとってかわり、机の上を所狭しと活躍するようになりました。コンピュータにおいては高価なこともありましたが、複雑な計算をするための道具としてしか認識されていませんでした。では当時のコンピュータの日本語環境はどのように推移していったのかと言いますと、まずは日本のコンピュータ環境にはに半角のカタカナがありましたので、半角で出力していました。たとえば

コクゴ サンスウ リカ シャカイゴウケイ

というふうに出力していたものです。ところが長い文章となると読解するのにかなり時間を要し、そのたびに漢字のありがたみを感じたものでした。それから一時期、高価な漢字ROMというハードウェアを取り付けたコンピュータにJISコードを入力して漢字を画面に表示させていましたので、表示させるためには職人肌が必要とされたのです。その煩雑な作業を解き放つために現在のジャストシステムがNEC PC-9801と言われるパソコンでの日本語変換に成功し、次第にパソコンへの乗換えが進んでゆきました。とは言え、パソコンのプリンタから出力される字体においてはワープロと比較するとお世辞でも美しいと言えるレベルではなかっただけでなく、あくまでも、ハードウエアの域から越えるものではなかったのです。しばらくしてIBMが世界中の言語をソフトウェア上に持って、それを変換するという画期的な手法を導入し、急速にパソコンが普及してゆきました。勿論、それだけが理由ではなく、パソコン通信からインターネットへの移行、直感的に操作が出来るMacintoshの環境やMS-DOSからWindows環境への移行、安価で美しくカラー出力できるインクジェットプリンター、高速なレーザプリンタの開発、LANの整備の拡充などが背景にあったことは周知の通りです。

 こういった歴史的背景からハードウェアに依存する文字は追加される必要もなく、キーボードに表示されている半角英数字と特殊文字だけで十分となり、ハードウェアとC言語の環境もそのまま1バイトを踏襲しているわけです。

それではchar型は文字としてだけ利用するものなのかと言えばそうではありません。もう一つには256通りの整数としての使い方があります。でも最近ではめっきりchar型を宣言する機会が減りました。後に学習する構造体というところで、4バイトや8バイトの型のみで宣言し構造体全体として4の倍数にしたいという時代の流れがあるからです。最近まで構造体では2の倍数と言うのが時代の流れであったのですが今では4の倍数が隆盛を極めています。そのため、これからchar型の後に学習する2バイトの整数の型であるshort型さえも肩身の狭い存在になってきていることも事実です。

  一体原因はなんなのでしょうか。

後に学習する構造体の中に1バイトのchar型や2バイトのshort型が入っていると、ある特定のシステムにおいて不具合が生じるという報告がされているからです。勿論すべてのシステムにおいてではないことをきっちりここで断っておきます。それから、高性能なコンピュータの低価格化とメモリの下落によりいつの間にか1バイトの型の宣言で済むところを不必要に4バイト以上使用しても気にする必要がなくなったことが理由に挙げられるのではないでしょうか。さらには技術者への教育の面で宣言の重要性を細かく説く機会が少なくなった、あるいはきっちり教える人も少なくなっているからではないかと思われます。

  下の図を見てください。bool型と違い1ビットから8ビットまで使用しますので、合計256通りの組み合わせが出来ます。正負の符号を割り当てていないので、初期の段階では-128~127となります。以前小数を表示させるところで%fのfloatは4バイトで32ビットあるうちの1ビット分に正負の符号を割り当てていると説明しましたが、charはそれを持っていないためにそういう範囲になるわけです。では、計算上0~255使うとなるとchar型以外の2バイトを使用する型を宣言しなければならなくなるのかといいますとそうではありません。unsigned char(符号なしchar型)を宣言すると256通りをすべて0~255までの0を含む正の整数に割り当てることが出きます。

char 型         (1バイト)        -128~127 

1

1

1

1

1

1

1

1

unsigned char 型  (1バイト)        0~255 

1

1

1

1

1

1

1

1


char型で整数を出力する記述方法

char a = -10;

printf(a = %d\n, a );

unsigned char 型で 整数255を出力する記述方法

unsigned char = 255;

printf(a = %d\n , a );

char型で文字列を出力する記述方法

char a = 65;

printf( a = %c\n, a );   // 半角の英数字を出力する際にはcという書式指定子を利用する

---------------------------------------------------------------------

char型                          lesson14.c

--------------------------------------------------------------------

/**************************

    char型

***************************/

#include <stdio.h>

void main()

{

             /****    宣言    ****/

           char a = 65;                   // char型を宣言し変数aに65を代入

char b = -10;                  // char型を宣言し変数bに-10を代入

unsigned char c = 255;      // unsigned char型を宣言し変数cに255を代入

  /****    出力部分     ****/ 

           printf("a = %c \n", a );    // 65に対応した半角文字を出力

           printf("a = %c \n", a + 1); // 66に対応した半角文字を出力

           printf("a = %c \n", a + 2); // 67に対応した半角文字を出力

           printf("b = %d \n", b );    // -10を出力

           printf("c = %d \n", c );    // 255を出力

          

           return;

}

---------------------------------------------------------------------

出力結果

--------------------------------------------------------------------

a = A

a = B

a = C

b = -10

c = 255

----------------------------------------------------------------------

【 問題7 】

1.   lesson14.cの宣言部分において、型の宣言と代入部分を分けて記述しても実行結果が同じになることを確認しなさい。

2.   lesson14.cの英数字出力においてABCが出力されているが、小文字のabcが出力できるように書き換えなさい。

3.   lesson14.cのb = -10をb = -256に変更するとどうなるか確認しなさい。

4.   lesson14.cのc = 255をc = 256に変更するとどうなるか確認しなさい。

5.   lesson14.cのchar a; と unsigned c;のそれぞれの変数aとcのサイズを出力しなさい。

(ヒント)

1.     lesson14.cにおいて型の宣言と代入部分を分けて記述する場合、型の宣言を3行記述した後、代入部分を3行書くのが通例である。型の宣言をして代入部分を書いて、また型の宣言をして代入部分を記述するといった仕方は一般にとられない。

2.     ROM部分にはアルファベットの大文字からセットされているので、小文字はその後である。65が大文字Aの開始位置であるから、その26文字後の92と考えるのはいい線いっているのだが、実際のところそこには特殊文字がはいっているので、もう少し後ろになる。

3.     0と表示されて確認終了では意味がない。なぜ0が入ってしまうのか理解しなければならない。

4.     これも0と表示される。なぜ0が入ってしまうのか理解しなければならない。ヒント4を理解できればヒント3を理解することができる。下の図を参考に257を代入して1が出力され、258を代入して2が出力されることが理解できれば十分である。

unsigned char 型  (1バイト)        

1

1

1

1

1

1

1

0

254

1

1

1

1

1

1

1

1

255

オーバーフロー                                256 

0

0

0

0

0

0

0

0

(1バイト目)

       

0

0

0

0

0

0

0

1

(2バイト目)

オーバーフロー                                257 

0

0

0

0

0

0

0

1

(1バイト目)

       

0

0

0

0

0

0

0

1

(2バイト目)

short型  (2バイト)

short型と言うのは2バイトの整数型である。2バイト分(256*256=65536通り)をすべて整数部に割り当てる。よってchar型と同じく正負の符号をもたないことからshort と unsigned shortを持つ。shortは-32,768~+32767の範囲の整数を扱うことができ、unsigned shortにおいては0~65535の範囲を扱うことが出来る。

short 型           (2バイト)    -32,768 ~ +32767 

1

1

1

1

1

1

1

1

1

1

1

1

1

1

  (1バイト目)

  (2バイト目)

unsigned short 型   (2バイト)        0 ~ 65535

1

1

1

1

1

1

1

1     

1

1

1

1

1

1

1

1

  (1バイト目)

  (2 バイト目)
    

int型  (4バイト)

int型と言うのは4バイトの整数型である。4バイト分(256*256*256*256= 4,294,967,296通り)をすべて整数部に割り当てる。よってchar,short型と同じく正負の符号をもたないことからint と unsigned intを持つ。intは -2,147,483,648~2,147,483,647の範囲の整数を扱うことができ、unsigned intは0 ~ 4,294,967,295の範囲を扱うことが出来る。昔は2バイトの整数型であったため、shortと同じ範囲であった。今でも環境によって2バイトか4バイトと認識が変わるため、プログラムの移殖に問題が生じることから、現在では4バイトの整数型であるlong型に取って代わられている。CやC++では極端に使用頻度が少なくなっているもののBASICやJavaやC#では未だに頻繁に使用されている型である。もし使用する際はまず自分の環境が2バイトなのか4バイト環境なのかsizeof()関数を使って確認する必要がある。

long型  (4バイト)

long型と言うのは4バイトの整数型である。4バイト分(256*256*256*256= 4,294,967,296通り)をすべて整数部に割り当てる。よってchar,short,int型と同じく正負の符号をもたないことからlong と unsigned longを持つ。longは -2,147,483,648~2,147,483,647の範囲の整数を扱うことができ、unsigned longは0 ~ 4,294,967,295の範囲を扱うことが出来る。ただし、int型の4バイトの変数との違いとして挙げられるのは、long型と他の型との区別をするために、変数の初期化において long a = 1L;と言う風に代入する整数の後ろにlong型頭文字の大文字Lをつける習慣が残っている点である。ただし、つけなかったからと言ってエラーするものでもない。 

---------------------------------------------------------------

short, int ,long型                 lesson15.c

---------------------------------------------------------------

/**********************************

    short, int , long   

**********************************/

#include <stdio.h>

void main()

{

           short a = 5000;

           unsigned short b = 60000;

           int c = 65000;

           unsigned int d = 1200000;

           long e = 65000L , f = 65000;                  //  Lあり Lなし

           unsigned long g = 1200000L ,h = 1200000; // L あり Lなし

           printf("%d %d %d %d\n", a, b, c, d);

           printf("%d %d %d %d\n", e, f, g, h);              // long d      

           printf("%ld %ld %ld %ld\n", e, f, g, h);            // long ld

           printf("intのサイズが2であればshortと同じ \

      \n4であればlongと同じ int = %d\n",sizeof(c));

           return;

}

------------------------------------------------------------------------------

実行結果

-------------------------------------------------------------------------------

5000 60000 65000 1200000

65000 65000 1200000 1200000

65000 65000 1200000 1200000

intのサイズが2であればshortと同じ

4であればlongと同じ int = 4

私ごとながらC言語を初めて学習した当時はまだint型が2バイトであった。当時は今と比べて決め事が多く、たとえば2バイト以下の整数を出力する時にはprintf(%d\n,a);という風に書式指定子にdを使用し4バイトのlong型の整数を出力する際にはprintf(%ld\n,a);と言う風にldと記述しなければならなかった。今の環境では、long型を扱う際にldではなくdにしてもコンパイル時に何のエラーも警告も出されない。またlong型の変数に値を代入する時も値の後ろに大文字のLをつけなければならなかったのだが、これもまた別段つけなくてもエラーも警告も受けない。もし学習者がエラーや警告を受けた場合は、まずsizeof()関数を使用してint型のサイズが2バイトか4バイトかをチェックすべきである。もしint型が2バイトの環境であればlong型の数字出力の際に書式指定子にldを用い、変数の初期化あるいは値の代入をする際には整数の後ろに大文字のLをつけることを薦める。

文字列の改行についてであるが、ダブルクォーテーション( )で閉じられた文字列を改行してコンパイルするとエラーが発生する。その時は改行前の最後尾にエスケープシーケンス(\)を記述することでエラーを回避することが出来る。

桁あわせであるがlesson15.cの出力結果を見る限り1行目と2,3行目の桁が合っていないので見にくくなっている。せっかくprintfのformattedの機能があるのだから、桁合わせの仕方を学習する。2行目の65000は5桁、1200000は7桁であるから書式指定子dの前に5と7をつけるだけで桁あわせが出来る。小数のところで小数点以下の桁あわせ、文字列のところで右寄せ・左寄せのフォーマットの仕方を再度実習するが、その前にこの機会を利用してまとめて説明することにする。整数部5桁、小数点以下の桁が2桁だった場合  %5.2f と記述する。また右寄せ・左寄せを逆にしたい場合は %-5.2fというふうにマイナスをつける。(fは小数点floatの略)

(修正前)

printf("%d %d %d %d\n", a, b, c, d);   

5000 60000 65000 1200000

65000 65000 1200000 1200000

65000 65000 1200000 1200000

(修正後)

printf("%5d %5d %7d %7d\n", a, b, c, d);

5000 60000   65000 1200000

65000 65000 1200000 1200000

65000 65000 1200000 1200000

 カンマ(,)で区切って宣言する方法についてであるが、short a ; short b ;という記述をshort a, b;と記述しても同じである。同様にshort a=0; short b=1;をshort a=0, b=1;と記述してもかまわない。コメントが必要であるかないかで使い分けたほうがよい。

【 問題7 】

1.     unsigned short a = 30000, b = 32000; と宣言した上で a + bの値とa のメモリサイズを出力しなさい。

2.     int a = 30000, b= 32000;と宣言した上で a + bの値とaのメモリサイズを出力しなさい。もしエラーが発生した場合、どのように記述を変更するのかを理由をつけて答えなさい。

3.     long a = 30000, b= 32000;と宣言した上で a + bの値とaのメモリサイズを出力しなさい。

4.     unsigned long a = 30000, b= 32000;と宣言した上でa * bの値とメモリサイズを出力しなさい。

5.     unsigned long a = 30000L, b=32000L;と宣言した上でa*bの値を編集拡張子ldを使って出力しなさい。

(ヒント)

1.     なぜshort a=30000, b = 32000が不適切なのか理解すること

2.     なぜint型の場合メモリサイズをチェックしなければならないのかを考えること。short型と同じ2バイトだった場合どうしてint型よりもshort型を利用されるのか考えること。

3.     long型のメモリサイズのshortより大きく、また4バイトのintと比べてもlong型が優先される場合があるのかを考えること。

4.     unsigned long にするメリットを考えること

5.     30000Lと記述するメリットを考えること、編集拡張子ldと記述するメリットを考えること

 標準のC言語で記述されたプログラムコードはどのコンパイラでもコンパイルでき実行でき、容易に移殖ができます。ところが、日頃C++のコンパイラでコンパイルしていると、確かにCを包含しているとは言えC++から可能になった記述をいつのまにかC言語の標準だと錯覚してしまうことがあります。まずコメントの // はC++から使用されたもので、確かにC言語でも使用できますが、システムによっては推奨されない場合があります。それから、int型を2バイトとしていた時代のプログラムコードが存在しますので、移殖の際には十分気をつけましょう。まず、sizeof()関数でシステムの環境が2バイトか4バイトかをチェックすることが大切です。チェックできない時は、プログラムコードのlong型で値が初期化されているときに大文字のLが数字の後ろについているか、float型で値が初期化されているときに大文字のFがついているか、さらには書式指定子にdの他にldが使われているかをチェックします。そういう記述があればそれに準拠した上で、int型の記述を避け、すべてlong型に統一することが大切です。

float型  (4バイト)   有効桁数 約7桁

float型と言うのは4バイトの浮動小数点数です。4バイト32ビットのうち1ビット分を正負の符号に割り当てているので、unsigned floatという宣言はなく、floatのみの宣言になります。残りの31ビット分をintやlongのように割り当てているわけでなく、指数部に8ビット、仮数部に23ビット分を割り当てているのです。それでは指数部と仮数部とはなんなのか説明してゆきます。

 正負の符号(+-) 1ビット           仮数部25ビット

     指数部 8ビット

 2進数の0.1を10進数に直すと0.5になる。次に2進数の0.01を10進数に直すと0.25になります。つまり逆数を利用しているわけです。

2進数 (1/2)の0乗 =   1    → 10進数    1

2進数 (1/2)の1乗 =   0.1   → 10進数   0.5

2進数 (1/2)の2乗 =  0.01     10進数  0.25

2進数 (1/2)の3乗 = 0.001     10進数 0.125

2進数の0乗1乗2乗3乗にあたる乗数が指数部です。

では仮数部は何なのかと言うと下記を見てください。

2進数で11111 →指数部 11111 である。これを指数部と仮数部で表記すると

11111*(1/2)の0乗になります。

2進数で1111.1 →指数部 11111 である。これを指数部と仮数部で表記すると

11111*(1/2)の1乗になります。

2進数で111.11 →指数部11111である。これを指数部と仮数部で表記すると

11111*(1/2)の2乗となります。

2進数で11.111→ 指数部11111である。これを指数部と仮数部で表記すると

11111*(1/2)の3乗となります。

そこで指数部を最初から(1/2)の127乗つまり2のー127乗を固定して考えようというのです。

そうするとfloatを2進数で表す時は

float = (正負の符号)*((1/2)の127乗)*(1.仮数部) となります。

この考え方により10進数の0.1を2進数で表そうと思っても0.000110011001100110011...と無理数の循環小数になってしまい、正確な値を得ることが出来ません。結局信頼できる桁数で値を丸めて近似値を取得しているのです。実際に計算すると、float型で信頼できる10進数の有効桁数は約7桁と言われているが、実際のところ6桁と導き出されます。

ここ数年前からパソコンにはCPUにFPUと言われる小数を専門に計算してくれる装置が付属しているので、4バイトのfloatとこのあと学習する8バイトでfloatの倍精度を持つdoubleとどちらが計算が速いかというベンチマークテストを行うとdoubleの方が速いという結果が出ます。こういった桁数の精度や演算スピードの問題から最近では4バイトのfloatに取って代わって8バイトのdoubleを使用するのが時代の趨勢です。しかしここに大きな落とし穴があります。実際に使用するシステムではFPUを搭載しているかと言う問題です。FPUがなければfloatもdoubleも演算速度に大差はありません。パソコンほどの大きなコンピュータであれば標準でFPUという装置が付属していますが、小さなマイコンレベルでは付属していないものがあります。こういったFPUのないものに、4バイトのfloatで足りる変数をすべて8バイトのdoubleを使用すると言うのは、いささか「散らかしすぎ」としか言いようがありません。int型がlong型に取って代わる正当な理由は見つかりますが、さすがにその場合には見当たりません。

初期のC言語では変数を初期化する際に float a = 1.4Fと言う風に値の後ろに大文字のFをつけたものです。最近の環境ではつけなくても問題はありません。ただし、既存のプログラムコードにそういった記述がある場合は、それに準じてコードを記述すべきです。また標準関数であるprintf()にはprintf(a=%f\n,a);というふうに書式指定子fを使用しなければなりません。その他、小数点以下の桁数を指定して出力する場合は、もし小数点以下2桁であれば%0.2fと記述するか%.2fと記述します。

float a = 3.141592;

printf(a=%0.2f\n,a);   あるいは printf(a=%.2f\n,a);

double型  (8バイト)   約15桁

double型と言うのは8バイトの浮動小数点数である。8バイト64ビットのうち1ビット分を正負の符号に割り当てているためunsigned doubleという表記は無くdoubleだけです。残りは指数部に11ビット、仮数部に52ビット分を割り当てているので、doubleはfloatより精度がよいことからfloatの単精度浮動小数点型に対し、倍精度浮動小数点型と呼んでいます。

 正負の符号(+-) 1ビット  指数部 11ビット        仮数部

 

                                 仮数部 計52ビット

     

標準関数で出力する際、書式指定しにはlfを使用します。  例printf(a = %lf\n,a );

----------------------------------------------------

float,double            lesson16.c

/*****************************

   float , long

******************************/

#include <stdio.h>

#include <math.h>           // 数学関数

void main()

{

float  a = sqrt( 2 );    // √2

double b = sqrt( 2 );    // √2

    printf("a = %1.6f\n" , a);   //  floatを整数部1桁、小数点以下6桁を出力

printf("b = %1.14lf\n", b);  //  doubleを整数部1桁、小数点以下14桁を出力

return;

}

------------------------------------------------

実行結果

------------------------------------------------

a = 1.414214

b = 1.41421356237310

-------------------------------------------------

数学関数sqrt() 平方根を利用する際にはmath.hを宣言しなければなりません。

他に代表的な例として以下の関数が挙げられます。

もし宣言しなかった場合は、定義されていない識別子です。というエラーが表示されます。

絶対値 abx() , xのy乗 pow(x,y) , 自然対数 log() , 常用対数log10(),

サインsin(), コサイン cos(), タンジェントtan()

【 問題8 】

1.   lesson16.cのprintf("a = %1.6f\n" , a);をprintf("a = %1.14f\n" , a); と書き換えて、つまり小数点以下6桁から14桁出力に変更しても、出力結果がdoubleの小数点以下14桁出力と相違があるか確認しなさい。

2.   lesson16.cのsqrt(2)をcos(3.141592)と書き換えて、floatとdoubleの出力結果の違いを確認しなさい。注意 三角関数の引数の単位はラジアンである。

3.   float a = 3.141592F , double b = 3.141592 , c ,d ;と宣言し、c = cos(a); d = cos(b);と代入して、cと d の値を出力しなさい。

4.   lesson16.cのdoubleをlong doubleと置き換えて宣言した場合どうなるか確認しなさい。

(ヒント)

1.   なぜ出力結果に相違があるのかの理由を考えなければならない。

2.   sin(),tan()も試してみて、精度の違いと有効桁数を把握しなければならない。

3.   longの場合 数値の後ろにLをつけ、floatの場合 数値の後ろにFをつけるという表記もあることを覚えておく必要がある。また書式指定子においてはlongの場合、ldを使い、floatの場合はf、doubleの場合はlfと記述する必要がある。

4.   マイクロソフトのC++の環境のみで利用できる。ただし今ではdoubleと同じである。

文字出力と文字列出力の違い

 

 次の章に移る前に文字出力と文字列出力の違いを踏まえておかなければいけません。C言語の文字出力は1バイトの英数字及び特殊文字を指し、1バイト以上の全角半角を含むひらがなやカタカナや漢字だけでなく英語圏以外の文字も出力することを指します。人間の脳では一文字か文字列かを区別するためにわざわざ前もって何かしら準備をする必要性はありません。が、しかしコンピュータの場合にはそれが1バイトの英数字及び特殊文字であるのか、1バイト以上の文字列であるのかを先に宣言しておかなければならないのです。その違いをコンピュータに理解させるために、1バイトの英数字及び特殊文字をシングルクォーテーションマーク()で Aというふうに囲み、複数の文字を組み合わせた文字列ではダブルクォーテーションマーク()でI love you.と囲むのです。

    (例) printf(I love you.\n);    // I love you.と出力する

printf(%s\n, I love you.); // I love you.と出力する

(注意 %sのsは文字列の書式指定子)

さらに文字列を変数で使用する場合には、その文字の最後尾に文字列の終了を意味する\0(エンドコード)が必要となります。‘\0はヌル null とも呼ばれ、ドイツ語の0に当たりますが、英語の0との大きな意味の違いは空、無をも含んでいる点にあります。

それではどうして文字列にエンドコードが必要なのか説明します。

printf(I love you.\n);とprintf(%s\n,I love you.);の両方ともI love you.と出力した後、その文字列はその後同じ関数内で再度利用されることはありません。となればI love you.が一時的に格納されたメモリのアドレスは、その後コンピュータの作業領域として使用されてもいいわけです。別の言い方をするとそのアドレスのデータが他のデータに上書きされてもかまわないことと同じ意味を持ちます。そこで文字列に自動的にエンドコード\0をつけて出力した後、そのメモリのエリアを自由に使えるように開放します。ところが文字列を変数で宣言したとなると、使用されている関数が終了するまで、そのデータを保持しておかないと再利用出来ないので、別の言い方をするとそのアドレスのデータが他のデータに上書きされてはいけないことと同じ意味を持ちます。

さてbool,char,short,int,long,float,doubleは固定で1バイト、2バイト、4バイトもしくは8バイトと決められているのに対し、その代わりとなる文字列の長さを一時保存しているメモリの存在があるのでしょうか。実はないのです。となればコンピュータが文字列と判断するには文字列の最後にエンドコード\0が必要となるのです。

 これからC言語で学習する上で最も難しいと言われているアドレスとポインタについて説明を簡略にします。後のアドレスとポインタの章で詳細に説明するので、ここでは理解できなくても読み流す程度でかまいません。

  整数でも小数でも文字列であっても、変数に値を代入した以上、メモリ上のどこかのアドレス(番地)に値が格納されているはずです。コンピュータは自動的に適切なアドレスを選択しデータを書き込んでいくので、たとえ変数がaであったとしても、開発環境や実行状況が変わればアドレスは変化していきます。それでもそのアドレスを取り出す方法があるので手短に説明します。ただ変数の前に&をつけるだけです。アドレスは16進数で表記されるのが一般的です。

(例)

#include <stdio.h>

void main()

{

char a =10;

printf("aのアドレスは%Xです。\n", &a );

return;

}

 逆にもしアドレスがわかればそこに格納されている値も取得できます。どうすればいいかというとただアドレスの前にアスタリスク*をつけるだけです。こういったアドレスに格納された値を取り出すことをポインタをあてる、もしくは簡略にポインタと言います。

#include <stdio.h>

void main()

{

char a =10;

printf("aのアドレスは%Xです。\n", &a );

printf("&aに格納されている値は%dです。\n",*(&a));

return;

}

 次に2バイトの場合です。全く同じなのですが、一つ気をつけなければならないことがあります。&で取り出された値は格納されている1バイト目のアドレスにもかかわらず、どうしてポインタをあてると2バイトの値がとりだせるのかという疑問にぶつかります。最初に2バイトのshort型を宣言した段階で、コンピュータ側はそれを2バイト分をひとまとまりにしているからです。

今度はint型の問題に触れましょう。int型を2バイトで実行している環境では変数の先頭のアドレスを見つけた後、その隣のアドレスをひとかたまりとして考えているわけです。ところがint型4バイトで実行している環境でそのint型2バイト環境のプログラムを実行すると変数の先頭のアドレスを見つけた後、次のアドレスだけでなく、海のものとも山のものとも知れぬ3番目と4番目の意味不明のデータまでひとまとめに使用してしまいます。またこの逆でint型4バイトで実行する環境で作られたプログラムコードをint型2バイトの環境で実行すると、先頭のアドレスから隣の隣の隣までをひとまとまりにしなければいけないのに隣まででひとまとまりをやめてしまうことから移殖の問題が生じるわけです。

#include <stdio.h>

void main()

{

short a = 1000;

printf("aのアドレスは%Xです。\n", &a );

printf("&aに格納されている値は%dです。\n",*(&a));

return;

}

同様に4バイトや8バイトを考えればわかることなのですが、どうしても理解できないのが固定ではない可変の文字列の場合です。まずchar型で変数aを宣言して1バイトの大文字のLを代入します。次に変数aのアドレスを取得します。それから、取得したアドレスに1バイトずつ後ろのアドレスにO,V,E,\0を代入します。そして最後に文字列を出力します。先に答えを言っておきますが、書式指定子sつまり文字出力の場合は引数に文字列を入れるのではなくて、先頭のアドレスを入れるわけです。書式指定子がsを持つ標準関数printf()は先頭のアドレスから順にエンドコード\0まで出力していくと言うことです。ですからサイズが必要なかったわけです。となると以前に学習したprintf(%s\n,I love you.);というのはI love you.という文字列ではなく、一時格納されたアドレスの先頭を渡していることになります。なぜエンドコードが必要ないかというとくどいようですが、変数として格納しているわけでないので、自動的にエンドコード\0をつけて出力しているだけです。

 

------------------------------------------------------

文字列         lesson17.c

------------------------------------------------------

/*********************************

    文字列

**********************************/

#include <stdio.h>

void main()

{

char a= 'L';         // Lを代入

printf("%X\n",&a);   // 変数aのアドレスを取得

*(&a+1) = 'O';       // Lの1バイト後に'O'を代入

*(&a+2) = 'V';       // Lの2バイト後に'Vを代入

*(&a+3) = 'E';       // Lの3バイト後に'E'を代入

*(&a+4) = '\0';       // Lの4バイト後にエンドコードを代入

printf("%s\n",&a);    // 文字列を出力

return;

}

-----------------------------------------------------------------------------------------

実行結果  (注意)Lのアドレスは環境や実行状況によって変化する

-----------------------------------------------------------------------------------------

12FF7C                        

LOVE

-----------------------------------------------------------------------------------------

【 問題9 】

1.     lesson17.cのprintf(%s\n,&a);をprintf(%s\n,&a+1);と置き換えて実行するとどうなるか確認しなさい。

2.     lesson17.cの*(&a+4) = '\0'; の1行を削除して実行するとどうなるか確認しなさい。

(ヒント)

1.     Oが格納されているアドレスから文字列をエンドコード\0まで出力しなさいと言う意味。

2.     文字列の最後につけなければならないエンドコードを省略すると、もし近くにエンドコード\0が見つからないとすれば、ずっと捜し続けるのでクラッシュする時もある。そのときは再起動しなければならない。

<まとめ>

bool型           1バイト 整数 0,1

char型           1バイト 整数 -128 ~ 127              c   or   d

unsigned char型   1バイト  整数 0 ~ 255                  c   or   d

short型        2バイト 整数 -32,768 ~ +32767           d

unsigned short型  2バイト 整数 0 ~ 65535                  d

(注意) int型の使用は推奨されない。

int 型         2 バイト 整数 -32,768 ~ +32767            d

unsigned int型    2バイト 整数 0 ~ 65535                  d

int 型         4 バイト 整数 -2,147,483,648~2,147,483,647    d

unsigned int型    4バイト 整数 0 ~ 4,294,967,295             d

long 型         4 バイト 整数 -2,147,483,648~2,147,483,647    d  or  ld    L

unsigned long型    4バイト 整数 0 ~ 4,294,967,295             d  or  ld    L

float型         4バイト 実数 -3.40282+38~ 3.40282E+38 

                                                                 f           F

  long型        8バイト 実数 2.225074E-308   1.797693E+308    f  or  lf   

 文字列                                            s

8進数                                             o

16進数小文字                                        x

16進数大文字                                        X

2.1 文と式/コメント

----------------------------------------------------------

2.データ型・演算子・式

 2.1 文と式/コメント

 2.2 データ型

 2.3 文字コード(ASCIIコード)表

 2.4 型変換

 2.5 演算子と評価順序

----------------------------------------------------------

 2.1 文と式/コメント

Chapter1.1で学んだことを今度は実際にプログラムを作りながら実践してゆきましょう。

最初はディスプレーにI love you.と表示させるプログラムです。

(注意)プログラムはアメリカ英語で作られていますのでプログラムコードを記述する時は、必ず半角の英数字を使用してください。

----------------------------------------------------------------------------------

I love you.と表示するプログラム lesson7.c

----------------------------------------------------------------------------------

#include <stdio.h>

void main()

{

  printf("I love you.");

  return;

}

-----------------------------------------------------------------------------------

実行結果

-----------------------------------------------------------------------------------

I love you.Press any key to continue

-----------------------------------------------------------------------------------

まずは、プログラムコードの先頭に、キーボード、ディスプレー、ファイルなどの入出力するための標準関数をプリプロセッサとして記述します。プリプロセッサの後ろには ; をつけないように気をつけましょう。

#include <stdio.h>

次にメイン関数を記述します。voidというのは空あるいは無いという意味ですがこれについては後に詳しく説明します。次に{ }の記述の仕方ですが、大きく分けて2種類あります。マイクロソフト社の開発環境ではデフォルトで関数の下に { を記述するようになっていますが、サンマイクロシステムズを始めvoid main(){ というふうに関数のすぐ後ろに { を記述するのが一般的です。別段自由なフォーマットで記述できるC言語の環境ですから、どちらが正しいとは一概に言えません。極端な話、関数が短い場合、void main(){    }と一行で書いてもいいわけです。

---------------------------------------------------------

マイクロソフト社の開発環境

---------------------------------------------------------

void main()

{

}

-------------------------------------------------------------------

サンマイクロソフト社を含む一般的な記述の仕方

-------------------------------------------------------------------

void main(){

}

そして最後に{ }の中に以下の2行を記述します。ただし、命令文の先頭には2マスあるいは4マス分開けるというインデント(段落)をつけてください。つけなかったからと言ってエラーになるということはないのですが、見にくければ必ず後からプログラム作成時にミスを誘発する大きな要因になることは言うまでもありません。4マス空けるのがマイクロソフト社の開発環境の初期設定ですが、サンマイクロソフト社を初め2マス空けるのが一般的です。

命令文の後ろに必ず ; をつけてください。命令が終了したことを意味します。

 printf( )のprintの後ろにくっついたfはformattedの略で、右寄せ、左寄せ、桁合わせなどのフォーマット出力の機能があることを意味します。半角の1文字の英数字を出力する際は

printf(A);という風にシングルクォーテーション( )で囲むのですが、文字列になると

printf(I love you.);という風にダブルクォーテーション( )で囲みます。

  returnがありますがこれについてもvoidと同様に後に詳しく説明します。今回のプログラムではこのreturn;を記述しなくてもエラーにはなりません。少し説明をするならば、メイン関数の戻り値(戻り値については後に説明します)がvoid(空)つまり戻り値がないので、returnが必ず必要とされないためです。しかしながら、戻り値がなくても記述するのが通例です。もし関数に戻り値が存在した場合、return 0;という風に戻り値をreturnの後に記述します。

 記述し終わりましたら、コンパイルして実行してみてください。

 もしコンパイルエラーが出たとしたら主に以下の原因が考えられます。

文字 '0x81' は認識できません。文字 '0x40' は認識できません。

というものです。これはインデントなどに全角のスペースが入力された場合です。エラーが出た行から全角スペースを取り除き、半角のスペースを挿入するか、TABキーでインデントをつけてみてください。

 もう一つ考えられるのは、#include <stdio.h>;という風にプリプロセッサの部分に ; を記述してしまったか、printf(I love you.)という風に命令文の後ろに ; を付け忘れてしまったということが考えられます。

 コンパイラによっては、#includeと<stdio.h>の間にスペースを入れなくてもエラーが出ないバージョンと出るものがありますが、必ず半角スペースを挿入するようにしましょう。

 他にケアレスミスとして、error C2001: 定数が 2 行目に続いています。とerror C2143: 構文エラー : ')' が 'return' の前に必要です。というメッセージが表示されたとしたら2つのダブルクォーテーションマーク(     )で文字列が閉じられていないことが考えられます。また、ダブルクォーテーションマークでなくシングルクォーテーション( )で文字列が閉じられていた場合は3 文字以上の文字定数があります。というエラーメッセージが表示されます。

 まれに例題と見比べても全く同じに見えるのにどうしてもエラーが出てしまい、ますますあせってしまいエラーの原因がわからなくなるときがあります。多分printf(“I love you”);という風にダブルクォーテーションマークを半角( )にすべきところを全角の( “ )にしてしまったとか、半角の括弧 ( ) であるところを全角の( )にしてしまった場合です。先ほどまでのエラーメッセージでは原因を把握しやすいのですが、こういった場合は意味不明なエラーメッセージを返してくるので気をつけましょう。

【 問題1 】

1.lesson7.cのvoid main()をmain()と書き換えてコンパイルするとどうなるでしょうか。

2.lesson7.cのvoid main()をvoid main(void)と書き換えてコンパイルするとどうなるでしょうか。

3.lesson7.cのvoid main()をmain(void)と置き換えてコンパイルするとどうなるでしょうか。

4.私の名前は~です。と自分の名前を入れて画面に出力するプログラムを作りなさい。

(ヒント)

1.  voidは空とか無いという意味です。省略可能です。

2.  voidは空とか無いという意味ですから、()の中が(int x, int y)という風に中身があるわけではないので空の状態です。一般に関数の()の中にはvoidは記述しません。

3.            1.2の混合なので実行結果は変わりません。

4.            esson7.cのI love you.を自分の名前に変えるだけですが、気をつけなければならないことがあります。半角文字ではない自分の名前を全角文字で記述した後、ダブルクォーテーションマークを半角にしなければならないのに、戻し忘れて全角のままにしてしまうことがあります。

次はコメントの書き方についてです。

------------------------------------------------------------------------

コメントの書き方       lesson8.c

------------------------------------------------------------------------

#include <stdio.h>

/*****************************************

**** I love you.と出力するプログラム  ****

**** 2006.12.24 by R.Hayakawa(kiitos) ****

*****************************************/

main(void)

{

  printf("I love you."); // I love you.と出力する

  return;

}

----------------------------------------------------------------------

実行結果

----------------------------------------------------------------------

I love you.Press any key to continue

----------------------------------------------------------------------

コメントの書き方は2種類しかありません。一つは複数行を一度にコメントする方法で

/*   で始まり   */ で終わる間に自由にコメントを記述する方法です。もう一つは

// と記述した右側の一行をコメントにする方法です。

 コメントされた部分はコンパイルされないので、メモリー使用量も実行スピードにも影響はありません。

 ただし、注意しなければならないことが2つあります。

一つはC言語が登場したときに、コメントする方法として /*  ~ */ のみだったのですが、C++が登場したときに // が追加されたわけです。となると古いコードを修正する際に、

// というコメントの使用が推奨されないこともあります。

それからもう一つは、// の後ろにはどんな文字を書いてもいいというわけではないということです。マイクロソフト社のVisual C++で // 出力表  や // 出力機能 と記述してもコンパイルエラーにはなりませんが、UNIX系のコンパイラではエラーになります。以下の表にある文字をコメントの最後に記述することを避けましょう。避ける場合は出力表ですとか出力機能ありというふうに文字列の間に入れて回避するのも一つの手です。理由はそれらの文字につまりエスケープシーケンス(\)と呼ばれる文字コードを含むからです。この\の使い方についてはこのあとすぐに改行の仕方についてで詳しく説明します。

Ы

ではエスケープシーケンス(\)の解説を交えて改行の仕方について説明します。

UNIXのシステムでは、サーバには // 、ローカルのディレクトリには / を使ってファイルの位置を表します。ローカルディレクトリでは、

/home/users/kiitos-jp/lesson/lesson1.c

というふうに記述し、またネットワークでは

//kiitosjpsvr/usrs/kiiots-jp/lesson/lesson1.c

というふうに記述します。実際にはMS-DOSやWindowsよりUNIXの方が歴史が長いにもかかわらず日頃使い慣れているWindowsパソコンのローカルディレクトリでは、

C:\Lesson\lesson1.c ネットワークでは  \\kiitosjpsvr\Leson\lesson1.c

というふうに記述されているため、http://www.kiitos-jp.comという記述の仕方を異質に感じられた人の方が多かったのではないかと思われます。

マイクロソフト社のビルゲイツはUNIXのディレクトリの表記に使用されている( / )に対抗して、にバックスラッシュ( \ ) (※表示できないため全角を使用しているが、実際には半角)を使用しました。ですからUSのPCでは

C: \Lessson\lesson1.cと表示されます。しかし、Windowsの日本語バージョンでは、この半角のバックスラッシュがすべて(\)と表示されるように設定されています。ですから、日本語キーボードの文字盤には未だにバックスラッシュが残っていますが、それを押下すると代わりに(\)が出力されてしまうという経緯があったのです。

エスケープシーケンスというのは、たとえば最初標準の黒色に設定されたプリンタが1~10まで印字する間に7から赤色で出力したいとなると、今までの連続した作業(シーケンス)から回避(エスケープ)して赤色を出力しなさいという命令を出さなければなりません。そういった連続した作業の間にピンポイントで別の命令を与えるのがエスケープシーケンス(日本語環境では\、US環境ではバックスラッシュ)となります。

改行をしたい時は\nと表記します。nはnew lineのnから取ってきています。

ブザーを鳴らすときは\aと表記します。aはalarmの頭文字です。

一文字前に戻すbackspaceを実行する時は\bと表記しますので、ブザーと間違えて\bとしないようにしてください。

それから\という文字を出力する時は\\と記述してください

あともう一つ大事なエスケープ文字があります。後で文字列を利用する際に再度説明しますが、文字列の最後には\0と記述しなければならないということです。

--------------------------------------------------------------------------------------

lesson9.c

--------------------------------------------------------------------------------------

/******************************************

****  I love you.と出力するプログラム      ****

******************************************/

#include <stdio.h>

void main()

{

           printf("I love you.\n"); // I love you.と出力する

           return;

}

----------------------------------------------------------------------------------------

実行結果

---------------------------------------------------------------------------------------

I love you.

Press any key to continue

-----------------------------------------------------------------------------------------

【 問題2 】

1.   私は~が好きです。と自分の好きな言葉を挿入して、実行時には改行出力できるようにプロ グラムを作成しなさい。

2.   上記のプログラムに/*  */を使って、表題・作成日・作成者を挿入し、出力する命令文の横に // を使って、私は~が好きですを出力する とコメントを記述したプログラムを作成しなさい。

(ヒント)

1.   誤ってprintf(“私はピアノを弾くことが好きです。\n);のようにダブルクォーテーションの外にエスケープシーケンスを記述しないようにしてください。

2.   /* の後ろと */ の前には必ず半角の1マス以上開けてください。同様に // の後ろにも必ず半角の1マス以上開けてください。くっつけてもコンパイルエラーにはなりませんが、読みにくくなるので、あけるのが一般的です。

今度は足し算、引き算、掛け算、割り算を学習しましょう。

---------------------------------------------------------------------------------

四則演算                 lesson10.c

---------------------------------------------------------------------------------

/*****************************************

****  四則演算                       ****

******************************************/

#include <stdio.h>

void main()

{

           printf("100+3=%d\n",100+3);  // 足し算

           printf("100-3=%d\n",100-3);  // 引き算

           printf("100*3=%d\n",100*3);  // 掛け算

           printf("100/3=%d\n",100/3);  // 割り算

           printf("100%%3=%d\n",100%3); // 余り

           return;

}

----------------------------------------------------------------------------------

実行結果

----------------------------------------------------------------------------------

100+3=103

100-3=97

100*3=300

100/3=33

100%3=1

------------------------------------------------------------------------------------

四則演算+ ― × ÷ は C言語ではそれぞれ半角の + - * / に対応します。

また整数の余りは半角の%に対応します。日常生活において、整数の余りを頻繁に計算する必要性はありませんが、コンピュータの世界では頻度の高い演算子です。

printf(1+1=%d\n, 1+1 );

%という書式制御は、関数の引数を参照し、dという書式指定子は-32768~-32767の範囲の整数を表すことが出来ます。

もしprintf(1+1=%dです。2+3=%dです。\n, 1+1, 2+3);

というふうに参照する引数が複数存在している場合、%という書式制御は左から順に参照します。

では%を出力したいときどうしたらいいのでしょうか。エスケープシーケンスのところでも説明したように%を出力する時は\\と記述するのと同様に%%と重ねて記述します

 ここから大事な説明に入ります。確かに四則演算を出力すること自体は難しいことではありませんが、C(C++を含む)言語を学習する上でどうしてもいつも2進数を知覚しながらプログラムを作っていかなければならないという側面をあわせ持っています。そこがJavaやC#といった最近の言語と一線を画す部分であり、学習者に多くの脱落者を生み出している大きな要因の一つです。

 1バイト8ビットでは256通りの組み合わせが出来ますが、2バイトでは256*256つまり65536通りの組み合わせが出来ます。当時のコンピュータのハードウェア環境で開発されたこともあり、編集演算子dは当初2バイト分の整数を出力するものでした。この2バイトの中に+と-を分ける正負の符号のビットが用意されることなく、すべて整数部分に割り当てられたため、65536通りが初期状態では-32768~+32767の範囲に割り当てられたわけです。最近の環境ではdに4バイト分(2の32乗 4294967296)割り当てていますから、実際には-2147483647~+2147483648の広い範囲で利用できるようになっています。この遷移した背景には、パソコンが普及を始めた頃のメモリーの価格と演算スピードにあります。メモリーを増設しようと思っても大変高価で、1MBあたり1万円と言われていた時代でした。とにかく1バイトでもメモリーの使用量を減らし、高速性を追求しなければならなかったわけです。最近では128MBのメモリーを増設しても1万円にも満たない市況価格ですから、当時と比べると100分の1以下まで下落したと言えましょう。

ここで疑問になるのが、小数をどのようにしたら表せるかという問題です。小数点を表す場合はdと同じく4バイト分の組み合わせができる浮動小数点のfloatの頭文字であるfを使用します。話がややこしくなるので詳しい説明を省略しますがfについてはdと違い、4バイトつまり32ビットのうちの1ビットに+か-を表す符号が割り当てられているのです。ではどうして整数と小数を同じような型で統一できないのかと言えば、整数を計算すると言うのはコンピュータから見れば簡単ですが、小数を計算するとなると苦手だからです。人間から見れば1の10分の1は0.1とすぐに答えを出せますが、コンピュータは1の10分の1を正確に計算することが出来なくて妥協して計算結果を出すからです。信じられないと思われるかも知れませんが、これから説明するコンピュータの小数の考え方を理解すれば納得できます。2進数で計算すると2進数の0.1が10進数の0.5にあたり、2進数の0.01が10進数の0.25にあたるように設計されているため10進数の0.1にあたる2進数が0.000110011001100110011.....と循環小数つまり無理数になっていつまでたっても答えを出せないからなのです。結局、信頼できる桁数で値をまるめて近似値を出力しているに過ぎないので有効桁以上の値は信頼性が乏しく、演算スピードにおいても同じCPUで計算すると整数ほどのスピードが出ないのです。昔は同じCPUで計算していたので非効率的でしたが、最近のCPUの大半にFPU(浮動小数点演算装置)と呼ばれる小数部分だけを計算する専門の装置がついているので効率化されています。また、CPUが小数計算の命令を出すと、FPUがその命令を受けて計算するので、コプロセッサ(co-processor)略してコプロとも呼ばれることもあります。

【 問題3 】

1.   20+6 , 20-6 , 20*6 , 20/3, 20%3をそれぞれprintf()関数を5回使って整数で出力するプログラムを記述しなさい。

2.   上記の計算をprintf()関数1回だけ使用して、引数にそれぞれの四則演算を代入して整数で出力するプログラムを記述しなさい。

3.   20/3を小数で出力しなさい。

(ヒント)

1.   printf(20+6=%d\n,20+6);というふうに5つ作ります。

2.   printf(20+6=%d 20-6=%d ......\n, 20+6 , 20-6 ,........);というふうに対象となる引数を5つ、%を5つ使用します。

3.   整数の出力では%dでしたが、小数点の出力では%fを使用します。

これから、2進数のシフト演算について説明します。四則演算に関して言えばC,C++だけでなく、Basic、Java、C#を問わずプログラム言語全般に出来るのは言うまでもなく当然なことですが、シフト演算となるとC,C++だけになります。これがどれほど重要な役割を果たしているか説明していきます。自宅を見渡すと洗濯機、冷蔵庫、電子レンジだけでなくあらゆるところでマイコンとかPICとか呼ばれるものがそれらの機器に内蔵され活躍しています。(今後マイコンという表記に統一します。)わずか数センチのチップのためにパソコンほどの大きさがないのですが、洗濯機ですと、洗濯物の量が多いか少ないか判断してそれに応じた水量を入れる、洗濯物が絡まったらブザー音を鳴らすといった命令を実行させるプログラムがその内臓メモリに書き込まれているわけです。小さなチップにプログラムが内臓されているとなると想像がつくと思いますが、メモリ量がパソコンに比べて極端に少ないわけです。たとえば洗濯機の運転時には、自動的に洗濯物の量をセンサーから、マイコンへその値を逐次送信しています。仮にアドレスB123番地に0x01(10進数でも16進数でも1)が初期状態で入っていたとして、ある一定量以上の洗濯物の量だと判断した場合0x02(10進数でも16進数でも2)にするとします。普通の感覚だと1+1すればいいと考えるのは疑いもなくもっともなことです。確かにパソコンほどの大きさであればその考え方も通用します。が、しかしマイコンやPICレベルで、四則演算をするとなると、メモリの使用量が多くなるだけでなく実行速度も遅くなることから、なるべくそういうプログラムの記述を避けるようにしています。その代わり2進数のビットの位置を以下のようにシフトさせるという方法が取られます。こういった制御系のプログラムをする際、一般にマシン語で直接記述する、アセンブリを利用する、あるいはC言語で記述してコンパイルしたものをマイコンやPICに書き込むわけです。したがって高級言語の中ではC言語がマシン語も利用できることから、大きな優位性の一つとなっているゆえんです。

    

標準量                    10進数 1 、16進数 1 、2進数 00000001

1 ビット目

                                

0

0

0

0

0

0

0

1

    

                     1 byte(バイト)

標準以上                   10進数 2 、16進数 2 、2進数 00000010

2 ビット目

                                

0

0

0

0

0

0

1

0

    

                     1 byte(バイト)

上記のようにビットの位置を全体的にシフトするほうが、四則演算するよりも高速でメモリ使用量が少なくなるのです。0x01を全体的に左側に1個分シフトするというのを式で記述すると以下のようになります。

             0x01 << 1

また0x02を右側に全体的に一個シフトすると

             0x02 >> 1

と言う風になります。

もし容量以上であれば 0x01 << 2と言うように左側に2個分シフトさせます。

同様に0x15を左側に全体的に3個分シフトするのを式で記述すると、0x15 << 3 、

また0x78を右側に全体的に2個分シフトする場合 0x78 >> 2 となります。

0x15   21     00010101

0

0

0

1

0

1

0

1

                                                 0xA8  168     10101000

1

0

1

0

1

0

0

0

0x78   120     01111000

0

1

1

1

1

0

0

0

                                                  0x1E   30     00011110  

0

0

0

1

1

1

1

0

では実際にプログラムを記述して検証してみましょう。

----------------------------------------------------------------------------

シフト演算             lesson11.c

----------------------------------------------------------------------------

/**********************************

****  シフト演算               ****

***********************************/

#include <stdio.h>

void main()

{

           printf("0x78 >> 2 = %d 10進数\n", 0x78 >> 2 ); // 10進数

           printf("0x78 >> 2 = %x 16進数\n", 0x78 >> 2 ); // 16進数小文字

           printf("0x78 >> 2 = %X 16進数\n", 0x78 >> 2 ); // 16進数大文字

           printf("0x78 >> 2 = %o  8進数\n", 0x78 >> 2 ); //  8進数      

           return;

}

-------------------------------------------------------------------------

実行結果

-------------------------------------------------------------------------

0x78 >> 2 = 30 10進数

0x78 >> 2 = 1e 16進数

0x78 >> 2 = 1E 16進数

0x78 >> 2 = 36  8進数

---------------------------------------------------------------------------

10進数で表す時はdという演算変換子を使用するというのを前回学習した通りです。では16進数と8進数を表示させるにはどうしたらいいのかといいますと、16進数を表示させるには2通ります。16進数を表記する際に頭に0xとつけることは最初の段階で学びましたが、printf()関数でアルファベットの部分(abcdef)を小文字で出力したい場合は0xとxを小文字にし、大文字にしたい時は0Xと言ふうにXを大文字にします(ABCDEF)。8進数においては「蛸(たこ)」のオクトパスの頭文字を取ってoを使用します。制御系でも16進数か2進数で表記するのが主流で、8進数で表記されたプログラムコードをみる機会がめっきり少なくなっています。たとえ8進数で表記されていてもWindowsに標準でインストールされているような関数電卓を使って簡単に2進数、8進数、10進数、16進数を相互に変換できますので心配する必要ありません。

【 問題4 】

1.   0x01 << 1 つまり 0x01を全体的に左に1個分シフトし、8進数、10進数、16進数で出力するプログラムを記述しなさい。

2.   0x02 >> 1 つまり 0x02を全体的に右に1個分シフトし、8進数、10進数、16進数で出力するプログラムを記述しなさい。

3.   0x15 << 3 つまり 0x15を全体的に左に3個分シフトし、8進数、10進数、16進数(アルファベット小文字)、16進数(アルファベット大文字)で出力するプログラムを記述しなさい。

(ヒント)

1.   1ビット目から2ビット目へシフトしても16進数で表記する際にアルファベットが出てくるほど大きな数ではないので、%xと%Xと両方記述する必要はないが、同じ値が返ってくるか確認する意味はある。

2.   上記と同様2ビット目から1ビット目へシフトしてもアルファベットが出てくるほど大きな数ではない。

3.   シフト演算によってアルファベットが表記されるので、小文字で表示させる場合は%xで表し、大文字で表示させる場合は%Xで記述する。

今度学習するものはビット計算と呼ばれるもので、これもC言語特有の演算で避けては通れないものです。これから挙げる例は実際の洗濯機のマイコンに記述されたプログラムコードとは違いますが、制御系全般ではこのような考え方をしているものと認識してください。たとえば洗濯機のセンサーから洗濯量を受信し、マイコンが標準サイズか標準以上のサイズかを判断した場合、脱水回数をそれぞれ3回と5回とに振り分けて設定すると仮定します。さて、メモリにどのように記述するかというと10進数で3か5つまり16進数でも0x03か0x05にあたる値を代入するわけではないのです。3回の場合は1ビット目、2ビット目、3ビット目に1というデータが入っている状態(00000111)にします。こういったビットに1を入れることをビットを立てるとかフラグを立てるという言い方をします。(今後フラグを立てるという表現に統一します。)5回の場合は、それに準じて1ビット目から5ビット目までフラグを立てます(00011111)。なぜここまでして回りくどい表記の仕方をしなければならないのかと言いますと、前回同様にメモリ使用量の削減と演算速度の高速化といった目的だけを理由に挙げるのは不十分で、実務レベルから見るとエラー処理及びエラー原因追求を重要なポイントに置いているからです。10進数の計算では何回目に正常終了して何回目に異常終了したかを回毎に情報を持たせることが難しくなります。無論出来ないことはないのですが、四則演算と条件式を組み合わせれば、不必要にメモリ量を消費してしまいます。

仮に洗濯物が標準量だった場合、脱水回数は最初に00000111と設定します。1回目の脱水を正常に完了した段階で、1ビット目を1から0に変更し(00000111→00000110)、2回目に脱水の時に洗濯物が絡まって完了できない状態になった場合は7ビット目にエラーを意味する(01000110)を立てた状態で、洗濯物の絡まりがなくなるのを待ちます。これを条件待ちあるいは到達待ちと言います。絡まりをほどいて運転を再開する際に時間短縮からこの2回目の工程をスキップ(skip)させるボタンを押したことにより3回目の脱水が終了後には00000010とメモリーに履歴が残るわけです。これによって1回目と3回目は正常終了しているが、2回目は完了しなかったという判断が出来るわけです。ここで条件待ちあるいは到達待ち以外のもう一つの状態を説明します。それは禁止処理あるいは置き換え処理と呼ばれているものです。2回目にスキップボタンを押して3回目の脱水工程へ遷移したものの、洗濯物の絡まりが原因でモータに負荷がかかりすぎて加熱していた場合、モータを緊急停止させて異常終了させます。メモリの8ビット目に異常終了のフラグを立て、10000110とメモリに履歴を残します。こうすれば2回目と3回目が完了せずに異常終了したということがわかるわけです。

3回                                        0x07    7     00000111

0

0

0

0

0

1

1

1

5回                                                    0x1F    31     00011111

0

0

0

1

1

1

1

1

3回中 1回目正常終了                                    0x06    6     00000110

0

0

0

0

0

1

1

0

条件待ち3回中 1回目正常終了  2回目実行中               0x46   70     01000110

0

1

0

0

0

1

1

0

3回中 1回目3回目正常終了2回目未完了                  0x02    2     00000010

0

0

0

0

0

0

1

0

異常終了3回中1回目正常終了2回目未完了 3回目未完了   0x86   134   10000110

1

0

0

0

0

1

1

0

ではどのようにしてフラグを立てたりして外したりするのでしょうか。

これもC言語特有の & と |といった代入演算子を利用します。& というのは論理積もしくはANDといいます。もう一方の | を論理和もしくはORと言います。どういう風に使えばいいかと言いますと下記を見てください。

下記のケースでは2進数のフラグが立っている位置を見ると、1ビット目から8ビット目まで共通している位置がないので論理積では00000000 0x00となります。しかし、論理和では双方どちらか、もしくはどちらともに1ビット目から8ビット目までフラグが立っている位置があればそこに1をフラグを立てますので010000110 0x46となります。

3回中 2回目実行中                                  0x06    6     00000110

0

0

0

0

0

1

1

0

条件待ち状態                                          0x40   64     01111111

0

1

0

0

0

0

0

0

論理和( | , OR ) 条件待ち 3回中1回目正常終了 2回目実行中  0x46  70  0x1000110   

0

1

0

0

0

1

1

0

                0x06 | 0x40 = 0x46

論理積( &, AND )                                                     0x00  00     00000000

0

0

0

0

0

0

0

0

                        0x06 & 0x40 = 0x00

それでは以下のケースはどうなるでしょうか。論理積においては双方の7ビット目の位置にフラグが立っているので01000000 0x40となり、論理和においては上段が下段を含んでいる状態なので、上段の01000110 0x70と同じになります。

条件待ち3回中 1回目正常終了  2回目実行中               0x46   70     01000110

0

1

0

0

0

1

1

0

条件待ち状態                                             0x40    64    11000000 

0

1

0

0

0

0

0

0

論理和( | , OR ) 条件待ち 3回中1回目正常終了 2回目実行中  0x46  70     01000110   

0

1

0

0

0

1

1

0

                        0x46 | 0x40 = 0x46

論理積( &, AND )            条件待ち状態                                0x40  64     01000000

0

1

0

0

0

0

0

0

                     0x46 & 0x40 = 0x40

では実際にプログラムを記述して検証してみましょう。

-----------------------------------------------------------------

ビット演算                    lesson12.c

-----------------------------------------------------------------

/**********************************

****  ビット演算               ****

***********************************/

#include <stdio.h>

void main()

{

           printf("0x06 | 0x40 = %x\n", 0x06 | 0x40 ); // 論理和 AND

           printf("0x06 & 0x40 = %x\n", 0x06 & 0x40 ); // 論理積  OR

           printf("0x46 | 0x40 = %x\n", 0x46 | 0x40 ); // 論理和  AND

           printf("0x46 & 0x40 = %x\n", 0x46 & 0x40 ); // 論理積  OR

           return;

}

---------------------------------------------------------------

実行結果

--------------------------------------------------------------

0x06 | 0x40 = 46

0x06 & 0x40 = 0

0x46 | 0x40 = 46

0x46 & 0x40 = 40

---------------------------------------------------------------

【 問題5 】

1.       00000111と 00011111の論理和と論理積を求めるプログラムを記述しなさい。ただし16進数でアルファベットを小文字と大文字の両方で出力しなさい。

2.       論理和を用いて00000110 の 8ビット目にフラグを立てて10000110を出力するように

プログラムを記述しなさい。ただし16進数でアルファベットを小文字と大文字の両方で出力しなさい。

3.       100000110と10000000の論理和と論理積を求めるプログラムを記述しなさい。ただし16進数でアルファベットを小文字と大文字の両方で出力しなさい。

(ヒント)

1.    00000111 = 0x07  ,   00011111 = 0x0F

2.    00000110 = 0x06  ,   10000000 = 0x80

3.    10000110 = 0x86  ,   10000000 = 0x80

1.5 Cプリプロセッサ

 1.5 Cプリプロセッサ

前に少しかじる程度にプリプロセッサについて説明しましたが、今回はよりくわしく説明します。

 マシン語でプログラムを作っていた頃は、すべて00~FFの命令を組み合わせて記述していました。ディスクに書き込むプログラムもグラフィックスで使用する数学の関数もすべて記述しなければいけなかったのです。しかしCのコードの先頭部分に#include <stdio.h>と記述して宣言するだけで、キーボードからの入力、ディスプレーへの出力、さらにはファイルへの読み書き(入出力)を可能にしてくれるエラーのない標準入出力関数を前もって用意してくれます。そのあと、どこどこのディレクトリに書き込みなさいと言った意味の命令文をを記述するだけで、コンパイルすると先に宣言部分がコンパイルされ、後から命令文もコンパイルされその両方がリンクして実行されるわけです。試しに画面上に文字出力させる命令文が箇所の後に#include <stdio.h>を宣言してコンパイルしようとするとどうなるでしょうか。結論から言うとエラーが発生します(lesson3.c参照)。このように先にを意味するプリと処理工程を表すプロセスが結合してプリプロセッサと名づけられています。

断っておきますが、stdio.hというのは宣言しているだけで実行する複雑なプログラムがこれに記述されているわけでありません。ただその実体のあるプログラムを読み込むために必要なのです。実体のあるプログラムは拡張子が.cで、定数や実体を読み込むために宣言するのがここで言うヘッダーファイルと呼ばれているもので拡張子が.hとなります。また、stdio.hのstdioは先ほどから説明していますように基本的なハードウェアとの入出力で使用するものですからSTandard Input and Outputが正しい略となります。紛らわしいのですが、決してstudioの略ではありません。

---------------------------------------------------------------------------------------------------------------

プリプロセッサを命令文の後ろで宣言した例      lesson3.c

---------------------------------------------------------------------------------------------------------------

void main()

{

           printf("I love you.\n");

           return;

}

#include <stdio.h>

-----------------------------------------------------------------------------------------------------------------

コンパイルエラーの内容

-----------------------------------------------------------------------------------------------------------------

'printf' : 定義されていない識別子です。

------------------------------------------------------------------------------------------------------------------

 C言語が普及し始めた頃、誰が言い始めたかは定かではありませんが、経験者が初心者に教える際に#include <stdio.h>をおまじないとかおまじないのようなものと説明したことが発端で、それ以来その表現が広まり頻繁に目や耳にするようになりました。私からみればおまじないであれば必要ないのですが、いつの間にかマシン語と高級言語との関わりが少なくなり、プリプロセッサについて必要以上に考えなくてもプログラムが作れるようになったというのが原因の一つではないかと推測しています。

 次に#include <math.h>の例を紹介します。たとえば数学関数でよく使われるsin(x),cos(x),tan(x)それから絶対値を表すabs(x)、その他xのy乗を表すpow(x,y)や√xを表すsqrt(x)をコードに記述する場合、#include <math.h>を宣言します。

もし宣言しなかった場合にはコンパイル実行時に 'abs' : 定義されていない識別子です。というエラーが表示されます。

------------------------------------------------------------------------------------------------------------------------

数学関数を使用した例                      lesson4.c

------------------------------------------------------------------------------------------------------------------------

#include <stdio.h>

#include <math.h>

void main()

{

           printf("-3の絶対値は%dです。\n",abs(-3));

           return;

}

----------------------------------------------------------------------------------------------------------------------

実行結果

----------------------------------------------------------------------------------------------------------------------

-3の絶対値は3です。

-----------------------------------------------------------------------------------------------------------------------

 さて数学関数を記述していないのに、不必要に#include <math.h>を宣言した場合コンパイルエラーになるのでしょうか。実はコンパイルエラーにはならないのです。ただしコンパイルが遅くなる、作成されたファイルが必要以上に大きくなる、実行速度が遅くなります。

理由は簡単でそのプリプロセッサをコンパイルしてしまったからです。大事なことは必要以上のものをプログラムコードに記述しない、言い換えれば「散らかさない」ということです。

 有名なヘッダファイルの代表的な例を2つ挙げるとstdio.hやmath.hの他に下記のものがあります。後に学習の途中で必要なヘッダファイルを説明しますので、今はこういうものもあるんだなというくらいで読み流してください。

   文字列をコピーするstrcpy(x,y)などを使用する場合

#include <string.h>

  乱数を発生させるrand()などを使用する場合

#include <stdlib.h>

 次に文字列の置換について説明します。

プログラムの中で定数を関数の中で宣言することはなるべく保守(メンテナンス)の面から避けられます。たとえば、システム全体で円周率を最初は3.14で利用していたのに、ある日3.1415に変更しようと決定された場合、もし関数の中にそれぞれ別個に宣言されていたら、すべて書き直す必要が出てきます。もし直しきれなかったら、大きなシステム上のエラーを引き起こしてしまいます。そこで、プログラムコードの先頭のプリプロセッサとして、その定数を宣言しておくのです。そうすると、その定数を変更するだけでプログラム全体でその値が有効となるからです。

ここでは #define pi 3.14と宣言して文字列pi を3.14に変換しています。試しにこの3.14を3.1415に変更すると出力結果は12.560000から12.566000へ変化します。

--------------------------------------

プログラム例        lesson4.c

--------------------------------------

#include <stdio.h>

#define pi 3.14

void main()

{

   printf("半径2の円周は%fです。\n",2*pi*2);

   return;

}

---------------------------------------

出力結果

--------------------------------------

半径2の円周は12.560000です。

 今度はマクロ定義について説明します。

プログラム中に明らかに簡単でなおかつ完全に正しい関数を定義することが出来ます。

どうしてここまでまわりくどい表現をしているのかを先に説明します。メイン関数であるmain()や自作の関数ではプログラム作成後デバグと呼ばれるプログラムミスを発見する手法でエラーを発見することが可能ですが、プリプロセッサの部分で誤りのある関数を作ってしまうと誤りを発見できないからです。ですから、あまり利用される頻度が少ないので、一応こういうものもプリプロセッサの仲間なんだなぐらいの知識で十分です。

たとえば、円周率が3.1415と設定して、あと半径を入れるだけの関数を

マクロ定義する際は以下のように記述します。

#define ensyuu(a) 2*3.1415*a

------------------------------------------------------

マクロ定義の例        lesson5.c

------------------------------------------------------

#include <stdio.h>

#define ensyuu(a) 2*3.1415*a

void main()

{

           printf("半径5の円周は%fです。\n",ensyuu(5));

           return;

}

-------------------------------------------------------

実行結果

-------------------------------------------------------

半径5の円周は31.415000です。                   

 

 最後に条件付きコンパイルについて簡単に説明します。

今まで紹介したプログラムの例では、すべてコンパイルされてしまいます。しかしながらプログラムの修正があり、修正前と修正後で実行時の動きを確認したい時はプログラムコードの中でコンパイルするところとコンパイルしないところを作り出す必要が出てきます。そこで条件付きコンパイルを利用するわけです。

下記の例では#define DEBUG 1が存在する時は#ifdefから#elseの間の部分がコンパイルされ#elseから#endifまでコンパイルされません。これを実行しますとI love you.と表示されます。今度は#define DEBUG 1の一行を削除してコンパイルすると、逆に#ifdefから#elseの間の部分がコンパイルされずに#elseから#endifまでがコンパイルされ、実行するとI need you.と表示されます。

------------------------------------------------------------

条件付きコンパイルの例        lesson6.c

-------------------------------------------------------------

#include <stdio.h>

#define DEBUG 1

void main()

{

#ifdef DEBUG

           printf("I love you.\n");

#else

           printf("I need you.\n");

#endif

           return;

}

------------------------------------------------------------

実行結果

-----------------------------------------------------------

I love you.

------------------------------------------------------------

1.4 Cコンパイラ

 1.4 Cコンパイラ

 前にも少し説明したかと思いますが、C言語をはじめJavaやC#などの高級言語で記述されたプログラムを直接コンピュータに実行させようとしても、コンピュータ側ではマシン語しか理解できないので実行できません。一旦それらをマシン語に翻訳してから実行させることになります。このことをコンパイルといい、コンパイルするプログラムをコンパイラといいます。

 コンパイラの代表的なものとして、マイクロソフトのVisual C++、Linuxに添付されているgcc、他にはIntel、HP(ヒューレットパッカード)のコンパイラなどが挙げられます。実のところ、基本的なC言語では問題なくどのメーカのコンパイラでコンパイルしてもエラーがなく実行出来るのですが、特殊な命令文を書くとコンパイルが出来るものとコンパイルエラーが出て実行できないものに分かれることがあります。あとは実行速度の違い、小数点の精度などに違いが表れます。普段使う分にはあまりコンパイラのメーカを気にせずとも問題はありません。

 次に下記のコード(lesson2.c)を見てください。

コメントが挿入されているのがわかります。コメントの書き方として /* で始まり、*/ で終わる複数行をコメントにすることが出来ます。また // で始める場合はその開始した場所から右側の一行分だけコメントにすることが出来ます。コンパイルするとこのコメントした部分はコンパイルされないのでメモリーの使用量や実行速度には影響されません。

次に進みましょう。{  }の前に段落がついて見やすくなっています。これをインデントといいます。マイクロソフト社の開発環境では半角4マス分空白を空けるというのが初期設定になっていますが、サンマイクロシステムズを初め一般的には半角2マス分インデントをつけるというのを推奨しています。しかしながら、コンパイルすると何行インデントをつけてもインデントの空白部分はコンパイルされないので、これもまたメモリーの使用量や実行速度に影響が出ることはありません。ということは、なるべくコメントやインデントをつけて見やすくするというのが重要なポイントになります。

近年ソフトウェアの巨大化にともなうマニュアル作成の時間削減、作成した関数などのチェックを目的に、コメント部分を読み取って自動的にマニュアルを作るというソフトが開発され一部のソフト会社で利用されています。それによってそのソフトが正しく読み取れるようにコメントの記述の仕方まで厳密な規則に従わなければならなくなってきていることも事実です。

-------------------------------------------------------------------------------------------------------

プログラム例           lesson2.c 

-------------------------------------------------------------------------------------------------------

#include <stdio.h>

#define pi 3.1415

/****************************************************

****   円周を求めるプログラム                    ****

****   2006.12.24    by Ryoichi Hayakawa(kiitos) ****

*****************************************************/             

void main()

{

  int i;

  for( i =0; i < 10; i++ )

  {

    printf("半径%dの円周は%fです。\n",i,2*pi*i); // 円周を出力する

  }

  return;

}

----------------------------------------------------------------------------------------------

出力結果

-----------------------------------------------------------------------------------------------

半径0の円周は0.000000です。

半径1の円周は6.283000です。

半径2の円周は12.566000です。

半径3の円周は18.849000です。

半径4の円周は25.132000です。

半径5の円周は31.415000です。

半径6の円周は37.698000です。

半径7の円周は43.981000です。

半径8の円周は50.264000です。

半径9の円周は56.547000です。

--------------------------------------------------------------------------------------------------

1.3 Cのプログラム

1.3 Cのプログラム

  マシン語によるプログラムを作る難しさから解放するために、いくつかの言語が改良されてC言語が登場しました。この言語は私達が中学校で習う英語レベルで、なおかつ自由な形式でプログラムを作ること(プログラミング)を可能にしたのです。

 C言語の構造は至って簡単です。大きく分けて宣言部分と命令部分に分けられます。

まず宣言部分ですが、たとえば自分一人で文章をフロッピーディスクやハードディスクに記録するプログラムを作るとなると大変な作業ですが、しかしながらそういうことをする必要は一切ありません。自分がファイルに書き込みなさいという命令を数行書くだけでそれを可能にするコードが前もって準備されているからなのです。それをプログラムの先頭に記述して宣言しておくだけでよいのです。これをプリプロセッサといいます。

下のプログラムコードでは#include <stdio.h>の部分にあたります。

また数学で使われる特殊な関数を利用したい場合には#include <math.h>と宣言しておきます。

他に、プログラム全体に円周率や固定の値を持つ変数を前もって宣言する場合は

#define pi 3.1415というふうに定義しておくこともできます。これを定数定義といいます。

次に命令部分にあたる命令文を記述する場所ですが、プログラムの中に一回だけ必ず記述しなければならないメイン関数つまりmain()という関数の中の{    }の中に記述します。他の関数においてはたとえばmenseki()など自由に名前をつけて必要に応じて追加していくことが出来ます。関数の語源から言えば、昔、函数と表記されたように{   }の箱(函)の中に命令文を記述すると表現したほうがわかりやすいのではないかと思います。下記の例にあるmain関数の中に、使用頻度の高い標準関数の一つであるprintfというのがありますが、読んで名のごとく出力するという意味です。printの後ろにfがくっついていますが、formattedの略で文字の配置(フォーマット)を指定して出力することができるという意味となります。注意しなければならないのは、命令文の終わりには必ず ; が必要だということです。けれどもC言語やC++では#include や #define で始まるプリプロセッサには ; をつけてはいけないのです。確かに紛らわしいので、後に開発されたJavaやC#ではこういった宣言部分にも ; をつけなければならないようになっています。それからreturnと記述されていますが、これについては後の章で詳しく説明します。今は意味がわからなくても記述しておかなければならないという程度の感覚で結構です。最後にファイル名の最後の拡張子には小文字のcをつけることに気をつけてください。lesson1.Cというふうに拡張子に大文字のCを記述すると一般的にC++のファイルと判断されるからです。例外としてマイクロソフトのVisual C++では拡張子にlesson1.cppというふうにcppをつけます。cppはc plus plusの略です。

---------------------------------------------------------------------------------

プログラム例        lesson1.c

---------------------------------------------------------------------------------

#include <stdio.h>

#define pi 3.1415

void main()

{

   printf("半径2の円周は%fです。\n",2*pi*2);

   return;

}

----------------------------------------------------------------------------------

出力結果

----------------------------------------------------------------------------------

半径2の円周は12.566000です。

----------------------------------------------------------------------------------

1.2 C言語

1.2 C言語

マシン語(機械語)の入力の仕方は00~FFまでの256個の命令で出来ると前回説明しましたが、ではどのように入力したのかといえば、コンピュータの中に0000からFFFFまでの箱があって、そこに直接タイプしてプログラムを作成し実行していたのです。(※8ビットパソコンの場合ではFFFFまででしたが、16ビット32ビットパソコンといった環境でも変わります。わかりやすく説明するためにFFFFと表記します。)あたかも表計算ソフトのセルの中に00からFFを入れるようなものです。

そのデータを格納する0000~FFFFの場所のことをアドレスまたは番地と言います。

アドレス  B000番地        B001番地        B002番地     B003番地     B004番地

DE

3F

86

AB

CC

 たとえば、上記及び下記の表を参考にするとABが格納されているアドレス(番地)はB003となります。注意しなければならないのは、表計算ソフトのように先頭の左上、コンピュータで言えば0000番地から順に自由に書き込むことが出来ないということです。途中からしか自由に書き込むことが出来ない理由は、アドレスの先頭部分からある程度のアドレスまでに四則演算を始めとした変更が許されていない情報が入っているからなのです。ある日、1+1を計算したら3が出てきたということがないように、その区間は読み込みだけが可能で、書き込みが出来ないようになっています。その読み込みしかできない記憶領域をRead Only Memoryと言い、略してROMといわれています。代表的なハードウェアの名前でCD-ROMがあります。それに対して、自由にデータを書き込むことが出来るメモリーの領域をRandom Access Memoryと言い、略してRAMと言われています。代表的なハードウェアの名前ではDVD-RAMがあります。

余談なのですが、CDを開発したのはオランダのフィリップスという電気メーカーです。当初ポケットサイズに入るディスクとして、60分程度の音楽を記録できる媒体を開発しCompact Discと名づけました。ところが当時の指揮者カラヤンから「これではベートーベンの交響曲第9番が収まらない。」とクレームをつけられ一回り大きい74分程度まで記録できる大きさに変更したという逸話が残っています。

 話を戻しましょう。コンピュータから見れば、00~FFまでの256種類の命令しかありません。パソコンが普及し始めた当時は、所有者の10,000人に1人しかマシン語でプログラムが組めないと言われていました。その後、人が自然に近い感覚で読み書きできる言語が開発され、BCPL言語を経て、B言語そしてさらに改良が加えられC言語が登場し、これがプログラム言語の主役の座についたのです。C言語の特徴は、今までの00~FFまでのマシン語も利用でき、なおかつ、スピードもメモリーの使用量もマシン語と余り変わらないといった優れものです。

 ここで注意しなければならないことがあります。プログラム言語にはC言語をはじめ、C++、Java、Basic、C#などのいろいろな高級言語がありますが、実は人間がわかりやすい言葉でプログラムを記述した後で、やはり00~FFまでのマシン語に変換してから実行しなければならないということです。つまりコンピュータからみればC言語を理解できず、00~FFまでの命令しか理解できないということを意味します。その高級言語で書かれたプログラムを自動的に00~FFのマシン語へ変換し適切なアドレス(番地)へ格納することをコンパイルといい、そのコンパイルするプログラムのことをコンパイラといいます。それではどうして、JavaやBasicやC#と言った言語が登場したのでしょうか。実はCやCを拡張したC++は今まで説明したきたようにアドレスを含むマシン語の概念がないとプログラムを組めないという難解さから、多くの学習者が途中で挫折してしまうといった問題をはらんでいたためです。その後、普段人間が使う言葉で、それらを考えなくてもプログラムが組めるようにとBasic、Java、C#などが開発されたものの、一方では代償も大きくなっている面も少なくありません。C言語やC++と他の高級言語たとえばBasic、Java、C#と比較すると後者の言語では演算スピードやメモリー使用量が犠牲になっています。確かに大容量メモリーの価格の下落、ハイスピードのコンピュータの低価格化という恩恵によって、それらの問題の穴埋めをしてくれていることも否定しませんが、しかし未だに高級言語の中でもC言語は不動の主役と言えます。どうしてかと言いますと最近では確かにCやC++言語以外の高級言語の割合が高くなってきていますが、C言語は完成度が高く、自分が作ったプログラムが正しければ、当たり前のことと思われるかも知れませんが正しく動作するからなのです。驚かれるかもしれませんが、他の高級言語はまだ改良中で、たとえ自分が作ったプログラムが正しくても、正しく動作しないことがあるのです。

 

 話は変わりますが、もう一つマシン語とメモリの関係を理解する上で日常誰もが一度は経験している例を紹介しながら説明します。ワードなどのソフトを使って文章を書いている最中に、ソフトが突然クラッシュして今まで書いた文章が消えてしまうことがあります。ところが、市販のソフトあるいは無償のもので、そのデータを魔法のように復旧してくれるレスキューソフトをしばしば目にします。原理は簡単で、パソコンの電源が入っている間、たとえばアドレスB021番地に半角の英数字Kが入っていたとしますと、データが同じアドレス(番地)に上書きされない限りメモリ上に存在するという特性を利用したものに過ぎません。

 少し話から外れますが、知識として入れておかなければいけない用語があるのでこの場を借りて説明します。電源が切れるとそのデータがなくなってしまうメモリーを揮発性メモリーといい、それに対し電源が切れてもメモリー上に長期間保存されているものを不揮発性メモリーといいます。普段よく見かけるUSBメモリーは不揮発性メモリーの仲間です。

0000

00

01

02

03

04

05

06

07

08

09

0A

0B

0C

0D

0E

0F

0010

Read Only Memory

0020

0030

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

AFFF

DE

3F

86

AB

0C

B000

Random Access Memory

B010

B020

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

FFFF

高級言語のメリット デメリット

メリット

デメリット

C、C++

高速、メモリー使用量が少ない。Windows,MacOS、UNIXで実行。

アドレスの感覚をつかむのが難しい。

BASIC

誰にでも簡単に作れる。

遅い、メモリーの使用量が多い。

エラーが多い。Windowsが主流

JAVA

ほとんど同じプログラムコードでWindows 、MacOS 、UNIX上で実行できる。

メモリーの使用量が多い。アドレスが使えないので、制御系には向かない。

C#

JAVAに似ていて、言語としては初心者でも開発できる。

Windows NT系(Windows XP,Windows 2000、Windows NT)のみ

 

1.Cとは  1.1プログラム言語

--------------------------------------------------------------------------------

Chapter 1】

1.Cとは

 1.1 プログラミング言語

 1.2 C言語

 1.3 Cのプログラム

 1.4 Cコンパイラ

 1.5 Cプリプロセッサ

--------------------------------------------------------------------------------

Chapter 1】

1.Cとは

 1.1 プログラミング言語

コンピュータ内部では電圧の高い信号と低い信号の2種類が存在し、その高い方を便宜上1とし、低いほうを0と割り当てて命令を行っています。このように0と1だけで表記する2進数をdigit(ディジット)といいます。電子機器のほとんどはこの2進数を利用しているのでdigital(デジタル)と呼ばれるようになりました。また0か1かといった最小単位を1bit(ビット)と言います。

                                               1 bit(ビット)

0

0

1

0

1

1

0

1

                     1 byte(バイト)

電圧が 低、低、高、低、高、高、低、高という信号があったとします。それを0か1で表記すると0,0,1,0,1,1,0,1となります。そして8つの信号つまり8bit(ビット)分をまとめて一つの命令にします。この8bitのまとまりを1byte(バイト)といいます。それから、1byte(バイト)を2進数で表示すると長くなって読みにくいことから、16進数の2桁の英数字で下記の表のように表記するのが一般的です。

(注意)10進数で10から16を表記すると2桁になるために、それぞれにA,B,C,D,E,Fを割り当てて1桁にしています。0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F(16進数)そして16進数と判別させるために数字の頭に0xをつけます。

0x00 , 0x01 , 0x02 ~,0x09, 0x0A, 0x0B , 0x0C , 0x0D , 0x0E , 0x0F

10進数2進数16進数10進数2進数16進数10進数2進数16進数 10進数2進数16進数

| 0| 00000000  0x00 

|16| 00010000  0x10 

|32| 00100000  0x20 

|48| 00110000  0x30

| 1| 00000001  0x01 

|17| 00010001  0x11 

|33| 00100001  0x21 

|49| 00110001  0x31

| 2| 00000010  0x02 

|18| 00010010  0x12 

|34| 00100010  0x22 

|50| 00110010  0x32

| 3| 00000011  0x03 

|19| 00010011  0x13 

|35| 00100011  0x23 

|51| 00110011  0x33

| 4| 00000100  0x04 

|20| 00010100  0x14 

|36| 00100100  0x24 

|52| 00110100  0x34

| 5| 00000101  0x05 

|21| 00010101  0x15 

|37| 00100101  0x25 

|53| 00110101  0x35

| 6| 00000110  0x06 

|22| 00010110  0x16 

|38| 00100110  0x26 

|54| 00110110  0x36

| 7| 00000111  0x07 

|23| 00010111  0x17 

|39| 00100111  0x27 

|55| 00110111  0x37

| 8| 00001000  0x08 

|24| 00011000  0x18 

|40| 00101000  0x28 

|56| 00111000  0x38

| 9| 00001001  0x09 

|25| 00011001  0x19 

|41| 00101001  0x29 

|57| 00111001  0x39

|10| 00001010 0x0A

|26| 00011010 0x1A

|42| 00101010 0x2A

|58| 00111010 0x3A

|11| 00001011  0x0B 

|27| 00011011  0x1B 

|43| 00101011  0x2B 

|59| 00111011  0x3B

|12| 00001100  0x0C 

|28| 00011100  0x1C 

|44| 00101100  0x2C 

|60| 00111100  0x3C

|13| 00001101  0x0D 

|29| 00011101  0x1D 

|45| 00101101  0x2D 

|61| 00111101  0x3D 

|14| 00001110  0x0E 

|30| 00011110  0x1E 

|46| 00101110  0x2E 

|62| 00111110  0x3E

|15| 00001111  0x0F 

|31| 00011111  0x1F 

|47| 00101111  0x2F 

|63| 00111111  0x3F

10進数2進数16進数10進数2進数16進数10進数2進数16進数10進数 2進数16進数

|64| 01000000  0x40 

|80| 01010000  0x50 

|96| 01100000  0x60 

|112| 01110000 0x70

|65| 01000001  0x41 

|81| 01010001  0x51 

|97| 01100001  0x61 

|113| 01110001 0x71

|66| 01000010  0x42 

|82| 01010010  0x52 

|98| 01100010  0x62 

|114| 01110010 0x72

|67| 01000011  0x43 

|83| 01010011  0x53 

|99| 01100011  0x63 

|115| 01110011 0x73

|68| 01000100  0x44 

|84| 01010100  0x54 

|100| 01100100 0x64 

|116| 01110100 0x74

|69| 01000101  0x45 

|85| 01010101  0x55 

|101| 01100101 0x65 

|117| 01110101 0x75

|70| 01000110  0x46 

|86| 01010110  0x56 

|102| 01100110 0x66 

|118| 01110110 0x76

|71| 01000111  0x47 

|87| 01010111  0x57 

|103| 01100111 0x67 

|119| 01110111 0x77

|72| 01001000  0x48 

|88| 01011000  0x58 

|104| 01101000 0x68 

|120| 01111000 0x78

|73| 01001001  0x49 

|89| 01011001  0x59 

|105| 01101001 0x69 

|121| 01111001 0x79

|74| 01001010 0x4A 

|90| 01011010 0x5A 

|106| 01101010 0x6A 

|122| 01111010 0x7A

|75| 01001011  0x4B 

|91| 01011011  0x5B 

|107| 01101011 0x6B 

|123| 01111011 0x7B

|76| 01001100  0x4C 

|92| 01011100  0x5C 

|108| 01101100 0x6C 

|124| 01111100 0x7C

|77| 01001101  0x4D 

|93| 01011101  0x5D 

|109| 01101101 0x6D 

|125| 01111101 0x7D 

|78| 01001110  0x4E 

|94| 01011110  0x5E 

|110| 01101110 0x6E 

|126| 01111110 0x7E

|79| 01001111  0x4F 

|95| 01011111  0x5F 

|111| 01101111 0x6F 

|127| 01111111 0x7F

10進数2進数16進数10進数2進数16進数10進数2進数16進数10進数 2進数16進数

|128| 10000000 0x80 

|144| 10010000 0x90 

|160| 10100000 0xA0 

|176| 10110000 0xB0

|129| 10000001 0x81 

|145| 10010001 0x91 

|161| 10100001 0xA1 

|177| 10110001 0xB1

|130| 10000010 0x82 

|146| 10010010 0x92 

|162| 10100010 0xA2 

|178| 10110010 0xB2

|131| 10000011 0x83 

|147| 10010011 0x93 

|163| 10100011 0xA3 

|179| 10110011 0xB3

|132| 10000100 0x84 

|148| 10010100 0x94 

|164| 10100100 0xA4 

|180| 10110100 0xB4

|133| 10000101 0x85 

|149| 10010101 0x95 

|165| 10100101 0xA5 

|181| 10110101 0xB5

|134| 10000110 0x86 

|150| 10010110 0x96 

|166| 10100110 0xA6 

|182| 10110110 0xB6

|135| 10000111 0x87 

|151| 10010111 0x97 

|167| 10100111 0xA7 

|183| 10110111 0xB7

|136| 10001000 0x88 

|152| 10011000 0x98 

|168| 10101000 0xA8 

|184| 10111000 0xB8

|137| 10001001 0x89 

|153| 10011001 0x99 

|169| 10101001 0xA9 

|185| 10111001 0xB9

|138| 10001010 0x8A 

|154| 10011010 0x9A 

|170| 10101010 0xAA 

|186| 10111010 0xBA

|139| 10001011 0x8B 

|155| 10011011 0x9B 

|171| 10101011 0xAB 

|187| 10111011 0xBB

|140| 10001100 0x8C 

|156| 10011100 0x9C 

|172| 10101100 0xAC 

|188| 10111100 0xBC

|141| 10001101 0x8D 

|157| 10011101 0x9D 

|173| 10101101 0xAD 

|189| 10111101 0xBD 

|142| 10001110 0x8E 

|158| 10011110 0x9E 

|174| 10101110 0xAE 

|190| 10111110 0xBE

|143| 10001111 0x8F 

|159| 10011111 0x9F 

|175| 10101111 0xAF 

|191| 10111111 0xBF

10進数2進数16進数10進数2進数16進数10進数2進数16進数10進数 2進数16進数

|192| 11000000 0xC0 

|208| 10010000 0xD0 

|224| 10100000 0xE0 

|240| 10110000 0xF0

|193| 11000001 0xC1 

|209| 10010001 0xD1 

|225| 10100001 0xE1 

|241| 10110001 0xF1

|194| 11000010 0xC2 

|210| 10010010 0xD2 

|226| 10100010 0xE2 

|242| 10110010 0xF2

|195| 11000011 0xC3 

|211| 10010011 0xD3 

|227| 10100011 0xE3 

|243| 10110011 0xF3

|196| 11000100 0xC4 

|212| 10010100 0xD4 

|228| 10100100 0xE4 

|244| 10110100 0xF4

|197| 11000101 0xC5 

|229| 10100101 0xE5 

|213| 10010101 0xD5 

|245| 10110101 0xF5

|198| 11000110 0xC6 

|214| 10010110 0xD6 

|230| 10100110 0xE6 

|246| 10110110 0xF6

|199| 11000111 0xC7 

|215| 10010111 0xD7 

|231| 10100111 0xE7 

|247| 10110111 0xF7

|200| 11001000 0xC8 

|216| 10011000 0xD8 

|232| 10101000 0xE8 

|248| 10111000 0xF8

|201| 11001001 0xC9 

|217| 10011001 0xD9 

|233| 10101001 0xE9 

|249| 10111001 0xF9

|202| 11001010 0xCA 

|218| 10011010 0xDA 

|234| 10101010 0xEA 

|250| 10111010 0xFA

|203| 11001011 0xCB 

|219| 10011011 0xDB 

|235| 10101011 0xEB 

|251| 10111011 0xFB

|204| 11001100 0xCC 

|220| 10011100 0xDC 

|236| 10101100 0xEC 

|252| 10111100 0xFC

|205| 11001101 0xCD 

|221| 10011101 0xDD 

|237| 10101101 0xED

|253| 10111101 0xFD 

|206| 11001110 0xCE 

|222| 10011110 0xDE 

|238| 10101110 0xEE 

|254| 10111110 0xFE

|207| 11001111 0xCF 

|223| 10011111 0xDF 

|239| 10101111 0xEF 

|255| 10111111 0xFF

  パーソナルコンピューターが登場して間もない頃は上記の表にあるように00からFFまでの256種類の命令を組み合わせて、たとえば3B,DA,9F,56という風にタイプしてコンピュータを実行していたわけです。これをプログラムを実行するといいます。

(簡単な例を挙げて、プログラムとはどういうことか説明します。たとえば、コンピュータに数字を入力すると、その値を読み込んで計算して画面に表示した後、不適切な値であればブザーを鳴らす命令の順序を記述したものをプログラムといいます。)

これらの00~FFを使ってコンピュータに命令する言語をMachine Language(マシン語 もしくは機械語)と言いいます。(注意)ただしこの00からFFまでのそれぞれの命令がコンピュータにどういう動作をさせるかについての説明は省略させていただきます。

 これではあまりにもわかりにくいので、これを「 mov 」(代入)、「 add 」(加算)といった表記をすることでプログラムできる、少しはマシン語より読み書きしやすいアセンブリ言語が開発されたわけですが、それでも一般の人に使いこなせるような言語とは程遠いものでした。これらのマシン語やアセンブリ言語は後に開発された一般の人にもある程度自然な文章で読み書きしやすいC言語、C++、BASIC、JAVA、C#などと区別するために前者を低級言語、後者を高級言語と呼んでいます。

-------------------------------------------------------------------------------------------

C言語の記述例

--------------------------------------------------------------------------------------------

#include <stdio.h>

void main()

{

  printf("I love you.");

  return;

}

--------------------------------------------------------------------------------

 次にコンピュータのスピードについて説明します。

先述のマシン語を1バイトずつ高速に処理するのがパソコンの中にある直径数センチのチップでCPU(中央演算処理装置)と呼ばれているものです。このCPUはIntel社のペンティアムを初めAMD社のAthlonや米国トランスメタ社のクルーソーなどが有名です。そのチップが組み込まれたボードをマザーボードと言います。CPUのスピードはクロック数で表され、最近ではノートパソコンでも1GHzを越えるものが一般的になりました。わかりやすく説明すると、マシン語の命令をクロック数1Hzで実行するとしますと1秒ごとに一回ずつ実行するわけです。1KHzですと1秒間に1,000回、1MHzですと一秒間に1,000,000回、さらに1GHzですと1,000,000,000回実行するわけです。こんなに高速で計算するものですから、CPUは多くの電気を消費するだけでなく、多くの熱を発し、その熱を機器の外へ放熱するために耳障りなファンが必要となったわけです。最近のパソコンでは空冷式のファンに代わって水冷式を利用した静かなものも発売され音に関する問題もかなり解消されてきています。発熱量が多くCPUが熱くなると処理スピードが落ちるという欠点がありますし、最悪の場合、冷却装置がその発熱量についてゆけず、CPUやマザーボード自体が壊れてしまうケースもあります。ところが数年前に熱を発しないで演算するという画期的なトランスメタ社のクルーソーと呼ばれるCPUが登場して一躍有名になりましたが、しかしながら何度も不都合が生じ、今ではあまりそのブランドを耳にすることも少なくなりました。

 さてクロック数だけで計算スピードを評価できるかといえば、実際には出来ないのです。

どうしてかといいますと、1+1=2などの整数の計算ではCPUのクロック数に依存しますが、小数点を含む計算や、複雑なグラフィックス関係の処理になるとそのクロック数に見合ったスピードが出ない場合があります。その他、CPUが同じでも、マザーボードの種類に左右されCPUの能力を最大限に引き出せないこともあります。最近では随分と液晶ディスプレーのスピードが高速になりましたが、一世代前の液晶たとえばDSTN液晶と呼ばれたディスプレーではCPUの演算後の表示に明らかに時間がかかっているのがわかるものもありました。確かに液晶自体反応速度においてはレスポンスがいいとは決して言えない表示機器のため、テレビの分野では反応速度の速いプラズマテレビがシェアの一角をなしています。

無論これらだけが原因ではありません。たとえCPUとマザーボード、ディスプレーが同じでもメモリー(RAM)の搭載量が違うと大きな違いが生じます。どうしてかといいますとプログラムした00~FFまでのメモリー消費量が最初から搭載されたメモリー(RAM)量で収まるのであれば問題ないのですが、収まらないことが多々あります。そうすると、どこにその収まらない分を、一時的に保存するかといいますと物理的にハードディスクという回転体に記録します。これを仮想メモリと読んでいます。それに比べメモリー(RAM)を増設すると、物理的に回転体を持たないチップのため高速になるわけです。

 パソコンが出始めた頃は8ビットパソコンと呼ばれるものでした。最近では32ビットが主流で64ビットも徐々にシェアを伸ばしてきています。さてこの8ビットや32ビットパソコンというのは一体何を意味するのか説明します。一回の命令で多くの情報を実行できればそれだけ、画面の色数、音質、複雑な計算が可能になります。わかりやすく言うならば、8ビットパソコンでは先ほどの表のように256(2の8乗)通りしか組み合わせができなかったので、ディスプレーはもっと多くの発色が可能だったにもかかわらず、当時は256色出力が限界だったのです。しばらくして2バイト分の16ビット分(2の16乗)を一度に演算するパソコンに進化し、256×256で65,536の発色を可能にするこに成功しました。それからあまり間をおくことなくハードウェアが16ビットから32ビットへ急速に進化し、換算すれば一度に4バイト分の4,294,967,296(2の32乗)の情報を演算することが可能になったのです。

  PC-8001というNECの8ビットパソコンは、後に登場した初代のPC-9801という16ビットパソコンより速かったのです。ここで一度に8ビットずつ計算するより、16ビットずつ計算する方が速いのではないかという錯覚に陥りますが、確かに一度に16ビットを計算すると言えども、ハードの処理スピードが遅いと8ビットより遅くなるのは自明の理です。それから16ビットの時代が長く続きます。Windows95が登場した当時は、すでにハードが32ビット化しているにもかかわらず、既存のWindows3.1やMS-DOSのソフトウエアの資産を犠牲にすることを避けるために、OSやソフト側が16ビットずつ計算して、擬似的に32ビット演算処理するハードウェアに対応していました。それから3年後、Windows98が登場し、OSもソフトも32ビットでネイティブに対応し、効率的になったことから高速になったわけです。最近サーバを含め、パソコンからも64ビット対応のハードが登場し、それに追従して64ビット対応のWindowsのOSが発売されました。勿論その他のソフトウェアも64ビット対応のものを購入しなければなりませんし、今までの32ビットのデータ資産が利用できるとは限りません。

 余談ですが、デジタルつまり0と1で電子機器が動作しているといったことを知らず知らずに日頃体感している実例をこれからFAXを例に挙げて説明します。

 送信側のFAXがもう一方の受信側のFAXと接続したとたん最初は低い音域でピーガーという音が鳴ったかと思うと、その後すぐに高い音域でピーガーと鳴るのをよく耳にします。最初の低い音域のピーガーは送信側のFAXの音で2種類しかないのです。高い方を1として、低い音を0として送信しているわけですが、ただ高速に高い音と低い音が入り混じって鳴っているのでピーガーと聞こえるだけなのです。後から聞こえてくる高い音域のピーガーは受信側のFAXの音でこれも2種類しかありません。これも同様に高い音の方を1として、低い音を0としているわけです。結局低い音域のピーガーの2種類の音と高い音域の2種類の音の計4種類しか存在しないことになります。

 FAXと同様に、0と1の2種類の信号でコンピュータも処理しているのに、なぜ計4種類の音が必要になったかと言いますと、もう一台のコンピュータと普通の電話回線で接続する際に音域が同じであると、どちらが送信側でどちらが受信側になるのかわからなくなるといった混信を避けるため低い音域の2種類と高い音域の2種類の計4種類の音が必要になったというわけです。こういった送信側のコンピュータ内部の電圧の高低を音の高低に変調(modulation)した後、再度受信側のコンピュータへ音の高低から電圧の高低に戻すいわゆる復調(demodulation)する変換機を当時Modulation and Demodulationと命名されましたが、後に読み方が短縮されて一般にモデム(Modem)と呼ばれるようになりました。

       パソコン        modulation and demodulation(modem)     パソコン

                       モデム        モデム

               電圧→音         音        音→電圧


                 電圧←音       音         音←電圧


 このモデムでは音の信号がノイズに弱い、音の信号を正確に認識するスピードの限界の問題から56,000bpsつまり一秒間に0か1の信号を最大56,000個までしか送受信できないというハードウェア上の限界にぶつかってしまいました。(56,000bit per secondの略 bpsをボーと発音する)その後、音に変換せずに直接電圧の高低をそのまま送信側のコンピュータから受信側のコンピュータに信号を送る技術が開発されました。いわゆるISDN(Integrated Services Digital Network)と呼ばれるものですが、安定的にはなったものの、まだまだスピード的に128kbps(128,000bps)が限界で実用に耐えるレベルには到達していませんでした。しかし、技術革新は目覚しくそれから間もなくしてADSLや光ファイバーが普及して今では100Mbps(100,000,000bps)という高速通信が可能になっています。

C言語の基礎 第十回

C言語の基礎 第十回 Leson10.c
目標:編集変換子を使って複数のデータを取り出せるようになる

[理論]
①関数printf()内の 複数に , が存在する場合、
最初の , で区切られた右側から順にデータを編集変換子%で取り出す。
②一文が長くなる場合は、カンマの部分で改行して見やすくするのが一般的である。
printf("%s %s %s","abcdefghijklmn","opqrstu","vwxyz");
(例)
   ① printf("%s %s %s","abcdefghijklmn",
        "opqrstu","vwxyz");
   ② printf("%s %s %s","abcdefghijklmn"
               "opqrstu",
               "vwxyz"   );

/****************************************************/
/****    C言語の基礎 第十回 Lesson10.c         ****/
/****    編集変換子(書式指定子)を覚える2      ****/
/****************************************************/ 
#include <stdio.h>

void main()
{
    printf("英語で%s %s %sと言います。\n",
      "I love you.","I need you.","I want you.");

    return;
}

[練習問題]
①私は~が好きですが~はあまり好きではありません。と~の部分を
 編集変換子を使って出力しなさい。
②日本の携帯電話の会社は~社と~社と~社です。~の部分を
 編集変換子を使って出力しなさい。
③私の名前では~です。私のふりがなは~です。また私のフリガナは~です。
を編集変換子を使って出力しなさい。ただし、一文が長くなるので改行して
記述しなさい。

C言語の基礎 第十二回

/*************************************************/
/**** C言語の基礎 第十二回  Lesson12.c      ****/
/**** 8進数に変換する                         ****/
/*************************************************/
#include <stdio.h>

void main()
{
    printf("30を8進数で表すと%oです。\n",30);
    return;
}

より以前の記事一覧

2021年7月
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
無料ブログはココログ