C言語によるプログラミングの基礎目次ポイント0:おまじない(教科書 p.5)ポイント1:文(一文ずつ処理,文末には「;(セミコロン)」)(教科書 p. 7)ポイント2:プログラムの処理順ポイント3:トークンポイント4:関数ポイント5:改行文字(教科書p. 10, 11)ポイント6:プログラムにおける空白・改行の扱い(教科書 p. 110)ポイント7:プログラムを読みやすくする工夫
このページでは,C言語によるプログラミングの基礎をまとめています.今後,より高度なプログラムを作成するにあたり,必ず理解しておかなければならないポイントです.しっかり習得しましょう.
今後しばらくは,下記のソースコードのうち /** **/ の部分の記述方法について勉強します.その他の部分について理解するには,もう少し勉強が必要となりますので,今度扱うことにします.ひとまず,下記については,決まり文句(おまじない)として,覚えておいて下さい.
xxxxxxxxxx
int main(void) {
/** **/
return 0;
}
プログラムは一文ずつ処理されます.C言語のプログラムでは,文(statement)の終わりに「;(セミコロン)」をつけます.日本語で文の最後に「。(句点)」をつけるのと同様に,「;(セミコロン)」をつけることで,一つの文が完結したことを表します.hello.cの場合
xxxxxxxxxx
printf("hello\n");
とあり,これが一つの分を表しています.
また,「おまじない」の内部で,何か処理を記述する際に「;(セミコロン)」を付け忘れると,エラーとなりますので,注意しましょう.
プログラムは上の文から順番に処理されます.例えば,下記のようなプログラム hello2.c を考えます.
xxxxxxxxxx
// hello2.c
int main(void) {
printf("hello\n");
printf("こんにちは\n");
return 0;
}
これを実行すると,
xxxxxxxxxx
printf("hello\n");
のあとに
xxxxxxxxxx
printf("こんにちは\n");
が実行されます.その結果,
xxxxxxxxxx
hello
こんにちは
のように,順に表示されます.
例えば,英語だと This is a pen. という文を読むときに,
「This」「is」「a」「pen」「.」
に分割して理解すると思います.また,これらは,
「This(主語)」「is(動詞)」「a(冠詞)」「pen(名詞)」「.(文の終わりを表すピリオド)」
のように,それぞれ役割をもっています.
仮に,「This」を「Th」と「is」に分解したとすると,この文を理解できなくなってしまいます.このように,英語を読み書きする際には,文を構成する「品詞」というものを暗に考えています.また,これを知っているからこそ,英語の読み書きができるのです.
C言語のプログラムの読み書きにおいても,文を構成する文字の並びのうち,意味的にこれ以上分割できないものが何か,ということを意識する必要があります.ソースプログラムに記述された文字の並びのうち,これ以上文法的に分割できないものを,トークンといいます.トークンとして,例えば以下のようなものがあります(教科書 p.108, 109):
キーワード
演算子
識別子
区切り子
文字列リテラル
定数
今回の演習では,「識別子」「文字列リテラル」「区切り子」のみを扱います.
例えば,
xxxxxxxxxx
printf("hello\n");
をトークンに分割して順に並べると,
xxxxxxxxxx
「printf」「(」「"hello\n"」「)」「;」
になります.また,
「printf」は識別子
「(」「)」「;」は区切り子
「"hello\n"」は文字列リテラル
です.
ポイント3-1:識別子
変数や関数に与えられた名前(変数や関数については,後ほど扱う予定.)
ポイント3-2:文字列リテラル(教科書 p. 9)
文字の並びを二重引用符(ダブルクオーテーション)「" "」 で囲んだもの.
重要:文字列リテラル内では日本語や全角文字を用いても良いですが,それ以外の場所では用いてはいけません.
ポイント3-3:区切り子
識別子や文字列リテラルなどを区切るために用いられる記号.以下の13種類.
xxxxxxxxxx
[ ] ( ) { } * , : = ; ... #
printfは関数の一つです (より詳細な資料はこちら)
プログラミングにおける関数とは,大雑把に説明すると,
与えられた入力に対し,特定の処理を行い,出力を返すもの
です.
イメージとしては,下図の中央にある「箱」のようなものです.
例えば,「与えられた二つの数字の足し算を行う」ことを
のように表すとします.このとき,中央の箱が「与えられた二つの数字の足し算を行う」関数を表します.この関数(箱)に15と37という数字を,実際に入力すると,
のような流れで,足し算の結果52が得られます.また,異なる二つの数字を入力すると,それらの和が出力されます.
皆さんは,関数の扱い方として
関数を作る(設計する・定義する)
関数を使う(関数に具体的な入力を与えて,出力を得る)
という二種類のことを学びます.
関数の使い方(よりフォーマルな説明は,教科書p. 6を参照)
関数を使う(実際に入力を与える)際,基本的には
関数名の後に丸括弧をつけ,
丸括弧の内部に,入力などを表す「実引数」を与えます.
実引数が複数ある場合は,「,(コンマ)」で区切ります.
xxxxxxxxxx
関数名(実引数1, 実引数2, ...)
具体例については,printfに関する説明を参照のこと.
先ほど,
xxxxxxxxxx
hello
こんにちは
の表示を行いました.ここでは「hello」と「こんにちは」の間で改行されており,プログラム(抜粋)は,以下のようになっております:
xxxxxxxxxx
printf("hello\n");
printf("こんにちは\n");
このように,「改行」を表示するためには,改行を表す改行文字「 \n」 を用います.
「\(バックスラッシュ)」と別の文字を組み合わせた特別な表記を,拡張表記といいます.「\(バックスラッシュ)」とそれに続く文字(改行文字の場合はn)の二つを合わせて,一つの文字として扱います.
バックスラッシュ「\」と円マーク「¥」
今後,ウェブ資料・スライド・参考書などで「\」が用いられていたり「¥」が用いられることがあるかもしれません.資料を読む際,これらは同じものだと解釈して下さい(著者の実行環境などに依存します.)
キーボードによっては,下図のように「\」がないものもあります.このときには,「¥」というキーを用いて下さい.Mac OSに関して,「¥」を用いてもエラーが出る場合は「option+¥」を用いて下さい.NSSOL Lab端末(Mac)では,「option+¥」で「\」を入力します.
下図のような,windowsのキーボードの場合,「\」と「¥」のどちらを用いても構いません.
以下では,プログラムにおける「空白」や「改行」の扱いに関する注意を述べます.hello.cでは以下に示すように,空白や改行を用いています.
黄色:空白,または,複数の空白
水色:改行
このような空白や改行があるため,プログラムが読みやすくなっています.
特に,printfやreturn 0 の手前の空白の列をインデント(字下げ)と言います(教科書p. 110).段落を作ってプログラムを読みやすくするテクニックです
しかし,完全に自由に空白や改行を行ってよいのではなく,下記に示すルールを守る必要があります.
文字列リテラル内では日本語や全角文字を用いても良いです.一方,文字列リテラル以外では,原則として,日本語や全角文字を用いてはいけません(コメント内は可).空白についても,全角半角の違いに注意して下さい,
例1)文字リテラル内の日本語・全角文字はOK.
xxxxxxxxxx
int main(void) {
printf("こんに ちは\n");
return 0;
}
例2)printfの「r」が全角になっている点と,(分かりづらいが,)printfの手前の空白が全角である点で間違い.
xxxxxxxxxx
int main(void) {
printf("hello\n");
return 0;
}
トークンは,文法上意味をもつ最小単位です.ですので,トークンを構成する文字の間に空白や改行をいれてはいけません.
ただし,
文字列リテラル内で改行してはいけません(空白の挿入は可.)
文字列リテラルにおいて,改行文字は一つの文字として扱うので,空白も改行も挿入してはいけません.
文字列リテラル以外の場所で,「(」「)」や「{」「}」などの間には,空白や改行を入れても良いです.
例3)識別子であるmainの間に空白がある点(半角空白でも全角空白でも間違い)と,識別子であるprintfの間に改行があるので間違い.
xxxxxxxxxx
int ma in(void) {
pr
intf("hello\n");
return 0;
}
例4)printfの後に空白,printf(の後に改行があるが,OK.
xxxxxxxxxx
int main(void) {
printf (
"hello\n");
return 0;
}
例5)下記のように,空白や改行をできるだけ入れないようにプログラムを記述してもOK(ただし,非常に読みにくい.)
xxxxxxxxxx
int main(void) {printf ("hello\n");return 0;}
例6)文字列リテラルである”hello\n”内に改行があるので間違い.
xxxxxxxxxx
int main(void) {printf ("hel
lo\n");return 0;}
これらは,文法上の規則であり,間違えるとコンパイルに失敗します.
コンパイル実行時にエラーが表示された場合は,上記のような間違いがある可能性がありますので,確認・修正の上,もう一度コンパイルして下さい.コンパイルできるようになっていれば,文法上の規則に関する間違いが修正できています.
慣れるまでは,教科書等のソースコードを真似るのが良いと思います.
下記のプログラムは,hello2.c のプログラムに,
/* hello2.cにコメントをつけたソースコード */
//ここでhelloと表示 その後改行
という二つの記述を加えたものです.
xxxxxxxxxx
/* hello2.cにコメントをつけたソースコード */
int main(void) {
printf("hello\n"); //ここでhelloと表示 その後改行
printf("こんにちは\n");
return 0;
}
と表示されます.ここから分かるように
/* */ で囲まれている部分と,
// から行末まで
に記載された内容は,プログラムの動作に影響を及ぼしていません.実際,ターミナルには表示されません.
また,文字列リテラルでないにも関わらず全角を用いていますが,コンパイルエラーにはなりません.
この部分を 注釈(コメント)といいます.
注釈は,プログラムの動作に影響を及ぼさないものの,プログラムをエディターなどで開けば読むことができます.これは,以下のような場合に便利です:
複雑なプログラムを作成するとき
複雑なプログラムほど,どこにどのような処理を記述したのか,分かりにくくなります.
このような時に,注釈を上手く活用しておけば,プログラム作成者自身にとっても読み手にとっても,読みやすいプログラムになります.
プログラムをデバック(プログラムの誤りを見つけて,修正)するとき
/* */ や // を使えば,プログラムの記述を残しつつ,その部分の処理をとばして実行することができます.
このように,注釈を用いてプログラムの一部の処理をとばすことをコメントアウトといいます.
具体例 )下記のプログラムについて,printfから始まる行のうち,どれか一行に間違いがあり,コンパイルエラーとなることを知っていると仮定します:
xxxxxxxxxx
int main(void) {
printf("A\n") ;
printf("A\n" );
printf("A\ n");
return 0;
}
ここで,どの行が間違っているか,手を動かして調べることを考えます. 例えば,二つ目のprintfが怪しいと思った場合,
xxxxxxxxxx
int main(void) {
printf("A\n") ;
//printf("A\n" );
printf("A\ n");
return 0;
}
のようにしてみます.このとき,コンパイルエラーが生じなければ,二つ目のprintfが間違っていたことが分かります.(実際は,そうではないので,二つ目のprintfから始まる行には不備はありません.)
以下,注釈を使うときのポイントについて,具体的に説明します(教科書p. 4-5)
/* */を用いる場合
行の途中に利用できる (/* が行頭になくても良いし,*/が行末になくても良い)
xxxxxxxxxx
// d1.c
int main(void) {
printf("2\n"); /*printf("2\n");*/ printf("3\n");
return 0;
}
xxxxxxxxxx
// d1.cの出力
1
3
/* と */ が,異なる行にあっても良い.
xxxxxxxxxx
// d2.c
int main(void) {
printf("1\n");
/*printf("2\n");
printf("3\n");*/
return 0;
}
xxxxxxxxxx
// d2.cの出力
1
/* を記述したが / を記述し忘れると,/ 以降が全て注釈内の文字列とみなされてしまうので要注意.
xxxxxxxxxx
// d3.c
int main(void) {
printf("1\n");
/*printf("2\n");
printf("3\n");
return 0;
}
→エラーが生じる
//を用いる場合
行の途中から利用できる(//を行頭に用いなくても良い)
/* */と異なり「閉じる」必要がない.
ただし,複数行注釈をつけたいときには,/* */ の方が便利.
xxxxxxxxxx
// g1.c
int main(void) {
printf("1\n"); printf("2\n"); //printf("3\n");
return 0;
}
以下の二つは,どちらも二つ目のprintfからの行と,三つ目のprintfからの行をとばして処理する(いずれも,1だけが表示される.)
xxxxxxxxxx
// g2_1.c
int main(void) {
printf("1\n");
/* printf("2\n");
printf("3\n"); */
return 0;
}
// g2_1.c
int main(void) {
printf("1\n");
//printf("2\n");
//printf("3\n");
return 0;
}