C言語のプログラミングは、関数を定義する作業といえます。
関数はある目的のために命令の記述を列挙して、そのまとまりに名前をつけたものです。
関数の中での命令の記述は、文(statement)を列挙することで作られます。
では、文はどのような規則で、作らなければならないものなのでしょうか?
また、どのような種類があるのでしょうか?
文を分類すると、まず次のように大きく宣言文と実行文の二つに分けることができます。(右端の欄が例)
そして実行文は、関数の中にしか作れなく、関数の中では実行文の後で、宣言文を書けない規則になっています。
宣言文 | 宣言した記憶領域を、メモリーに作る指示です(実体を作る)。同時に記述以降でその名前を使えるようにする。(ローカルとグローバルのものがある) | int i=0; char char_array1[2048]; |
|
実体を作るのではなく、記述以降で宣言した名前を使えるようにするための宣言(関数プロトタイプ宣言やグローバル変数を参照する宣言で、ローカルとグローバルのものがある) | extern double sqrt(double ); extern char char_array1[]; |
||
実行文 | 単純文 | 関数定義内で、実行を指示するもので、最後に; 『セミコロン』をつけます。(すべてローカルでグローバルはありません) そしてif や while などの対象にできます。 | i = 2047; char_array1[i]='\0'; printf( "%c", char_array1[i]); return; ; /* 空の文 */ |
複合文 | 関数定義内で、{ と、 } 囲んだ文で、{ }(ブロックと呼ばれる)の中に、文を並べた形で、宣言文や実行文を並べることができます。(すべてがローカルで、中で宣言した変数はローカル変数になります。) そしてif や while などの対象にできます。なお宣言文は、実行文の前で並べなくてなりません。 | { int d;/* このブロック内だけ使える */ if( scanf("%d", & d) != 0) return; char_array1[i] = (chra)d; } |
|
構造文 | 関数定義内で使い、if() 文、 while()文、do 文 while(); for(;;)文 switch()文 などが使えます。文には、一つの実行文を指定できます。(すべてがローカルです) | if(i >= 0) while(i < 2048){ int d;/* このブロック内だけ使える */ scanf("%d", & d); char_array1[i] = (chra)d; } |
以上のように複合文の中に、単純文を書く構造なので、単純文が全ての実行文の基本になります。
単純文は、必ず ; の『セミコロン』を付けて、一つの文を終結させます。
どんな単純文があるのでしょうか?以下で表にまとめます。
表現方法 | 表現例(プログラムに意味はありません) | 備考 |
---|---|---|
式 を書いて ; を付ける。 | i++; k = k % 10 -1; n = scanf("%d", & i) != 0; |
式とはデータを表現するもので、定数や変数単体、戻り値を持つ関数、及びそれらと演算子を使ったデータを表現するものすべてです。 |
; だけ | ; |
実行する指示はないので、空文(カラブン)と呼ばれます。しかし1つの文です。 |
関数呼び出し表現に; を付ける。 | printf("%e", 123.0) ; srand(123) ; |
値を表現しない void 型関数の呼び出し表現にも使えます。 |
goto文 | goto SATRT ; goto LOOP_END ; |
LabelNameのラベルが存在しなければなりません。なお、全ての実行文にラベルを付けることができます。しかし 文がない所にラベルを作れません。よって、ブロックが終わる直前へ移動するgotoでは、空文を作り、その前でラベルを書きます。 |
return文 | return; return i + 'A'; |
関数の処理を終わらせて、関数の呼び出し元へ戻ります。void型関数は戻り値を書きません。 それ以外は戻り値を表現する式を書きます。 |
break文 | break; |
繰り返しを強制終了する命令です。(switch内の文を終わらせる場合にも使います) |
continue文 | continue; |
繰り返し範囲の残りをスキップして繰り返しを継続させる命令です。 |
if() 文、 while()文、do 文 while();の処理対象で扱うのは一つの文です。
単純文は、一つの文なので、{ } 付けない次のような表現が書けます。
( i の添え字が指し示すchar_array1配列の要素が 0 でない間、 i を増やして指し示す位置を進める繰り返しです。)
while(char_array1[i] != '\0') i++;
または、
while(char_array1[i] != '\0') i++;
です。これら上記の部分プログラムは2つとも同じで正しいのですが、 これを間違えて次のように書いてしまうと、 無限ループで終わらないプログラミングになります。
while(char_array1[i] != '\0') ; i++;
上記では、繰り返し対象が;の空文一つで、なにもしないことを繰り返す指示です。 つまり、条件が成立すると、i++; を実行することはできません。
次のようにわざと空文を使って書く人がいるかもしれません。(正しいプログラミングです。)
while(char_array1[i++] != '\0') ;
確かに昔のコンパイラなど、コンパイラの能力によって少し効率のよい翻訳をしてくれる場合があります。 しかしそれは昔のことです。現在では分かりやすく、修正しやすくプログラムを書くということが、 最も大事な時代です。そうすると、間違いやすい単純文で構造文を作るのではなく、次のように複合文にした方がよいでしょう。
while(char_array1[i] != '\0') { i++; }
コンパイラは、どのように文を解釈するのでしょうか?コンパイラの処理ステップは次のようになっています。
次のコードにおいて、字句解析で得られた文字列を列挙するボタンです。⇒
次のステップの構文解析で、トークンのリストから構造を再構築します。
そしてif や while や for直後のトークンが、再び構造指定なのか? ; の単純文なのか? または{ }の複合文なのか解析して、
を作ります(フォルダアイコンは、ダブルクリックで開きます) 。
一度バラバラにするので、ソースプログラムでは、記述順番だけでプログラムにおける処理順番の構造が確定するように文法の規則が作られているのです。