第1回 ガイダンス・C言語のおさらい (2024年9月23日)


今日の内容


ガイダンス

授業の概要

現象数理学科の「プログラミング演習2」では、プログラミング言語としてC言語およびPythonを用いて、 コンピュータグラフィックスを含むプログラミング技術を学びます。 特に、様々な現象の数理モデルのシミュレーションの学習を通して、データの可視化およびコンピュータグラフィックスを学び、 論理的思考力とプログラミング能力の向上を目指します。

成績評価

成績評価については、以下の3点の合計で総合的に評価します。


重要:macOS のアップデートについて

2024年9月に、macOS のメジャーアップデート(15, Sequoia) がリリースされました。 OS のメジャーアップデートを行うと、本授業を受講するために別途作業が必要になる可能性があります。 OSのメジャーアップデートは他の授業で使う予定のソフトウェアへの影響等も考えられるので、 積極的には推奨しません (自分で対応できるなら、妨げはしません)。

なお、現在のバージョンを使い続けても全く問題ありませんが、 OSのセキュリティアップデートは随時行ってください。


この授業の目標

現象数理学科での学習を進めていく上で、コンピュータを用いた数値計算方法の習得は 大変重要になります。 ただし、その為には

といったことが必要となります。 このうち、プログラミング言語の習得については、プログラミング演習1においてC言語とPythonの基礎を習得済みです。

計算結果の可視化については、C言語とPythonで少し状況が変わってきます。 プログラミング演習で学んだように、Pythonでは簡単に計算結果を可視化することができます。 しかし、C言語には標準化されたグラフィック機能はありません。 この授業では計算結果の可視化には、C言語から利用できるGLSC3Dというグラフィックスライブラリを用います。 標準的とは言い難いグラフィックスライブラリですが、利用のために必要となる知識が最小限であることから、これを用います。

前半(1~7回)の授業では、主にGLSC3Dグラフィックスライブラリを用いて、数値計算結果の可視化方法と 数理モデルの簡単なシミュレーションについて学んでいきます。 後半(8~13回)の授業では、主にPythonのmatplotlibライブラリを用いて、数値計算結果の可視化方法と 様々な現象のシミュレーションについて学んでいきます。

演習では、数値計算法の習得も目標の一つではありますが、数値計算の必要性や、それを道具として使うという姿勢について、 ある程度理解することも大きな目標です。


授業の進め方

プログラミング言語の習得において個人的に一番重要だと考えているのは、 自分で考えてプログラムを書き、自分でプログラムの間違いを見つけて修正することです。 誰かに正しい書き方を教えてもらい、それをマネする事は学習の初期段階では必要でしょう。 しかし、ずっとそれを続けているだけではいつまでたっても自分一人でプログラムを書くことはできるようにはならないでしょう。

他の物事を学ぶ時も同じですが、間違えることで学べることは正解して学べることよりもはるかに多いです。 プログラミングにおいては、間違った書き方をすればコンパイル時にエラーが出たり、正しい正解が返ってきません。 そのときに何が間違っているかを考えることで、非常に多くのことを学ぶことができます。

この授業は「演習」なので、この「自分で書いて、自分で間違いを修正する」プロセスを重視しています。 とは言え、慣れないうちは何が間違っているのか分からないということが頻繁に起こりえます。 ですので、考えて分からない場合は遠慮なく、教員やTAの方に質問をしてください。 学生同士で教え合うことも実は非常に良い学習になるので、積極的に行ってくれてOKです。 ただし、単に友達のプログラムをコピペするのはやめてください。 その場合、どちらがどちらのプログラムを写したのかわからないため、両者ともにカンニング扱いにせざるを得ないです…。

また、chatGPTなどの利用については 大学の方からもアナウンス されていますが、生成された内容を理解せずにそのままコピペして提出することは厳禁です。 上記と同様に、カンニング相当の扱いとします。 疑わしい提出物(内容の説明がきちんと書かれていないもの)がある場合には、 別途提出物の内容について試問を行うことがあります。


ターミナル上でのコンパイルと実行(復習)

まずは、ターミナルを起動します。ターミナルは、

アプリケーション -> ユーティリティ 

にあります。それをダブルクリックして起動しましょう。 頻繁に利用するので、Dockに追加しておくと良いでしょう。

ターミナルを起動すると、白地のウィンドウが現れます。 これを、今後も「ターミナル」と呼ぶことにします。

カレントフォルダ(カレントディレクトリ)

ターミナル上で作業を行う際、フォルダ(ディレクトリ)の概念を理解する必要があります。 MacOS上でフォルダの操作をしますが、フォルダの中にさらにフォルダが入っており、階層構造になっている事がわかります。 このような階層構造(ツリー構造)によってファイルを管理しています。 古くはディレクトリと呼びましたが、最近はフォルダと呼ぶことも多く、これらは同義語と捉えて良いでしょう。

MacOS上のウィンドウを使った作業では、フォルダをクリックすることでフォルダの移動をしますが、 ターミナル上での作業においても、フォルダの概念があります。

ターミナルを起動すると、プロンプトが表示されますが、そのプロンプトに続いて、

pwd

と打ち込み enter キーを押すと例えば、

/Users/ev00000

等と出てきます。ev00000 の部分は自分の学生番号(ユーザー名)になっていると思います。 ターミナル内で命令(コマンド)を実行するには、このようにコマンド名を入力し、 enter キーを押します。 このような文字ベースのインターフェースを CUI (Characte User Interface)と呼ぶことがあります。 一方、フォルダをクリックしてフォルダ移動といったグラフィカルなインターフェースをGUI(Graphical User Interface)と呼びます。

さて、先の pwd コマンドで表示される場所をターミナル内での「現在の場所」という意味で、カレントフォルダと言います。

/Users/ev00000

/ で区切られていることにまず注意して下さい。/ はフォルダの区切りを示します。 つまり、 ev00000 というフォルダは Users というフォルダの中にあるという意味になります。

/Users

の最初の / は特別な意味があり、最初の / はフォルダ構造のルート(根っこ)という意味です。 つまり、フォルダ構造のルートを頂点としたフォルダ構造があるという事になります。

また、

/Users/ev00000

は、各ユーザーのホームフォルダと呼ばれ、各ユーザのファイルが納められる場所です。

フォルダを移動する場合は cd というコマンドを使います。 皆さんがこの演習で作成するプログラムは、全て「書類」フォルダ内の「prog2」フォルダに納めるようにするために、 フォルダを作成してみましょう。 (ターミナル等の操作に慣れている人は、自分のわかりやすい場所に保存しておくのでも構いません。)

まずは、ホームフォルダ内のファイルの一覧を表示してみましょう。

ls -p

として enter キーを押します。すると、例えば、

DUs-MBA11i7-M2011:~[19]%> ls
   Applications/		Dropbox/		Pictures/		auto/			lib/			sandbox/
   Desktop/		Library/		Public/			bin/			mysrc/			zandbox/
   Documents/		Movies/			Sites/			demo/			pkg.tmp/
 Downloads/		Music/			Temporary Items/	include/		sample/

このような出力が得られます。もっと沢山の文字が表示される人もいるでしょう。例えば、

Documents/

となっており最後に / がついていますが、これはフォルダであるという意味です。 この「Documents」フォルダが、GUI上で「書類」とされるフォルダです。 単に、ls とだけ入力した場合には / が表示されないことに注意しましょう。 他にも、ls -lp と入力するとフォルダの詳細についても表示されることも確認してみましょう。

それでは、「Documents」フォルダ内に「prog2」フォルダを作成するために、 Documents フォルダに移動します。

cd Documents

カレントフォルダを移動するコマンドが cd です。改めて pwd とすると、

/Users/ev00000/Documents

と「Documents」フォルダ内に入ったことがわかります。 ls -p としてみましょう。「書類」フォルダ内のリストが表示されます。 まだ「prog2」フォルダを作成していなければ、当然 prog2/ は表示されませんので、 以下のように入力してフォルダを作成しましょう。

mkdir prog2

これで、「prog2」フォルダが作成されたので、もう一度 ls -p と入力し、prog2/ が表示されることを確認しましょう。

次に、

cd prog2

で、「prog2」フォルダに移動しましょう。 今後はこのフォルダ内にソースファイルを作成し、 このフォルダ内でコンパイルや実行を行うことになりますので、 場所を忘れないように注意してください。 より分かりやすく整理するために、「prog2」フォルダ内に「20200921」(今日の日付)フォルダを作り、 授業や演習課題で作成したソースファイルは日付ごとのフォルダに分けておくと良いかもしれません。

ターミナルでのコンパイル

ターミナルで Cコンパイラを起動し、C言語のソースプログラム(例えば、 test0.c というファイル) から 機械語のプログラムを作成するには、次の様にします。

cc  test0.c  -o  test0

この命令によってC言語のソースプログラム test0.c から機械語のプログラム test0 を作成します。 -o test0 の部分を省略すると、a.out という機械語の実行ファイルができることも思い出してください。 できあがったプログラム test0 の実行は、やはりターミナル上で次の様にします。

./test0

ターミナル上で利用する主なコマンド等については、ターミナルでのファイル操作等にまとめておきました。


注意

ちなみに、ソースプログラムを作成するテキストエディタについては、 自分の好みのエディタを使ってください。 プログラミング演習1で使っていたエディタにこだわる必要はありません。 自分でうまく使えないものを使い続けて作業効率が落ちるのは、ナンセンスです。 テキスト入力できれば、メモ帳でも構いません。 (テキストエディタの選択については、宗教論争になるのでここでは深く追求しません)


コンパイルについての少し詳しい話(おまけ)

コンパイルによる機械語プログラムへの変換と、機械語プログラムの実行についておさらいしましたが、 もう少し詳しい話について、コンピュータの基礎知識と合わせて以下に書いておきます。 授業では触れませんので、各自読んでおいてください。

コンピュータの基礎

 コンピュータの話となると、ハードウェアソフトウェア といった言葉がよく使われます。 友達との会話で、「うちのパソコン、ハードが古くってさぁ」とか、「私、あまりソフトもってないから・・」といった会話もあるでしょう。 ハードウェア、ソフトウェアは何もコンピュータの世界だけの言葉ではありません。 例えば、DVDやBlu-ray Diskというものがありますが、DVDの再生装置が発売された当初、「ソフトの充実が望まれる」といった話がありました。 (今現在では充実していると言って良いでしょう) この場合、DVDの再生装置がハードウェアで、映画などが収められているDVDディスクがソフトウェアです。 (ディスク自体というよりは、その中に収められている情報(映画など)) もちろん、ゲーム専用機(PlayStation5やSwitch等)やスマートフォンも同様です。 語弊があるかもしれませんが、ハードウェアはソフトウェアの再生装置だと思っておいても良いでしょう。 最近は、ソフトウェアと情報はしばしば同じ意味として扱われます。

 さて、コンピュータの世界では、ハードウェアとは文字通り硬いもの、つまりパソコンなどの機械本体のことをさします。 コンピュータソフトウェアとしては、ワープロソフト(Word等)、表計算ソフト(Excel等)等がおなじみでしょう。 最近のスマートフォンはまさしく一昔前のパソコンと同等以上の性能を持ったコンピュータです。 携帯電話においても、メールソフトやWeb閲覧ソフトウェアが動いています。 なお、コンピュータソフトウェアは別名プログラムとも呼ばれます。 プログラムは「計画」や「予定」と訳すことができますが、コンピューターはプログラムに書かれている指示どおりに作動する自動機械ともいえます。 なお、コンピュータの日本語訳は電子計算機で、その名のとおり、計算をする機械です。 この演習ではその本来の使い方でる数値計算をコンピュータにさせることを目標としています。

 プログラミングとはソフトウェアを開発することであり、 プログラミング言語とは、ソフトウェアを開発するための言葉です。 プログラミング言語とは言語という名が示すとおり、文法があります。 しかしながら、自然言語(日本語や英語)のような複雑さはありません。 出てくるキーワードもわずかです。C言語もそのようなプログラミング言語の一種です。

 皆さんのこれまでのコンピュータの使い方は、他人の作成したソフトウェア(アプリケーションともよばれる)を利用する事だったでしょう。 例えば、メールソフトをつかったり、Webブラウザでホームページを閲覧したり、日常的にコンピュータを用いない人は皆無と言っても良いでしょう。 しかしながら、そういったソフトウェアの開発をする人は殆どいません。 この演習で皆さんに行ってもらう事は、簡単なソフトウェアではありますが、まさしくソフトウェア開発です。 そのようなクリエイティブな事に挑戦できるという意気込みで取り組んでください。

 余談ですが、実際数理系学科を卒業してソフトウェア関連の会社に就職する人も少なくありません。 ソフトウェア会社に入ってからも当然プログラミングの教育は受けますが、 今の速い段階である程度の基礎を身につけるかつけないかは大きな差になると思います。

 サイエンスとしての数学、数理科学という立場から、コンピュータと数学について少し考えてみましょう。

CコンパイラとC言語

 コンピュータは機械語とよばれる言語しか実行できません。 日本人が日本語しか理解できないのと同じことです。 日本人が英国人と話をしたい場合、日本人である我々が英語を習得する方法と、通訳をとおして話をする方法があるでしょう。 Cコンパイラとは、C言語から機械語に翻訳を行う通訳だといえます。

 日本語と英語は両方とも自然言語であり、言語として大差ありません。 しかし、機械語とC言語の間には大きな差があります。 機械語に比べてC言語は人間にとって格段に理解しやすい言語なのです。 C言語などのプログラミング言語を高級プログラミング言語と呼びますが、 高級とは機械語に比べてより人間が理解しやすいという意味で高級であるということです。 機械語にほぼ一対一で対応する言語としてアセンブラ言語というものがありますが、 現在も今後もアセンブラ言語に接することはまず無いと思います。 アセンブラ言語は、低級プログラミング言語と呼ばれることもあります。 (もちろん、低級プログラミング言語では低級なことしかできないというわけではありません。 どのようなプログラミング言語でかかれているにしろ、最終的には機械語に翻訳されるわけですから・・。)

 C言語とは、先述のように高級プログラミング言語の一種です。 高級プログラミング言語にはC言語の他に、Python, BASIC, Java, Visual BASIC, Delphi, PASCAL, LISP, FORTRAN, PROLOG, PL/I, C++, COBOL等々、それこそ山ほどあります。 その他にアセンブラ言語という低級プログラミング言語もあります。 高級プログラミング言語にはそれぞれ文法があり、その文法に従ってプログラムを記述する必要があります。

 繰り返しになりますが、コンピューターは直接的には機械語しか理解できません。 つまり、機械語で書かれたプログラムしか実行することができないのです。 もちろん我々ががんばって、機械語のプログラムを書けばよいのですが、それは大変でかつ効率もよくありません。 そこで、高級プログラミング言語であるC言語が登場します。高級プログラミング言語は、機械語に比べて、 より人間に理解しやすい言語となっています。

 そこで、高級プログラミング言語であるC言語でかかれたソースプログラムを 機械語に変換する必要があり、 その為のソフトウェアがCコンパイラと呼ばれるソフトウェアです。 (ソフトウェアの開発の為にC言語を用いるのですが、その為にCコンパイラというソフトウェアを使うのです。)

 ところで今回、皆さんはたまたまC言語を習っていますが、高級言語どれか一つをマスターすれば、 他の高級言語はたいした苦労も無くマスターできます。

 余談ですが、CコンパイラというソフトウェアもC言語でかかれています。(卵が先か、鶏が先か?)

(参考)Python や Java は?

Python や JavaはC言語のように一端機械語にコンパイルされた機械語プログラムを実行するのでは無く、 Python や Javaで書かれたプログラムは実行時に仮想マシン (仮想マシンはソフトウェアでハードウェアの違いを吸収する仕組みです。 よって、Python や JavaのプログラムはMacでもWindowsでもLinuxでも動きます) が理解する言語に変換しながら実行します。 これをインタプリタと言います。 実行と翻訳が同時に行われ、かつハードウェア上のソフトウェアである仮想マシン上で実行されるため一般に実行速度が遅くなります。 ただし、実行速度を上げるための様々な工夫がなされた環境もあり、一概には言えません。

一方、コンパイラ形式(C言語など)では、ソースプログラムを解析し、 より実行速度が上がるように時間をかけて機械語に変換することができます。 よって、速度が求められるアプリケーションについては、コンパイラ形式の方がむいていると言えます。 (ただし、大学の講義内で活用する限りにおいては、ほとんど差はないでしょう)


ソースプログラムの作成

ソースプログラムとは、まさにプログラムの元であり、 通常テキストファイル(文字ファイル)です。 ソースプログラムは、C言語やFORTRANといった高級言語でかつ、それら言語の文法に従った形式で記述されています。 次に示すものは、C言語で書かれたソースプログラムの例です。

#include <stdio.h>
int main()
{
  printf("Hello!\n");
  return 0;
}

C言語のソースプログラムがかかれているファイルには、最後に .c をつける決まりがあります。 プログラムの内容は後ほど説明しますが、画面に Hello! と表示するプログラムになっています。 プログラミング演習1の一番最初にやったはずです。

注意
ちなみに、\ は 例えば VScode 上では\(バックスラッシュ)と表示されているはずです。 windows 等では、\ と\は区別されていないため \ をそのまま入力すれば良いのですが、Mac では2つの記号が明確に区別されています。 2つの記号の違いについて、ここでは詳細を述べませんが、この web サイトや参考書で \ と表示されている箇所は、 VScode 等で入力する際には\に置き換えてください。 \は、option + \ で入力できます。 ちなみに、この web サイトのソースプログラムをコピぺして エディタ 上に貼り付けた場合には、きちんと\として表示されるはずです。


コンパイルエラーについて

先のプログラムを次のように書き換えてください。

#include <stdio.h>

int main()
{
 plintf("Hello!\n");
 return 0;
}

見て分かるように、printf 関数のつづりが間違っています。

このままコンパイルしようとしてもエラーとなり、機械語の実行ファイルは生成されません。 このように、C言語に限らず、プログラミング言語では、つづり間違いなどは許されません。 このようなコンパイル時に出るエラーのことを、コンパイルエラーと呼びます。

また、次のようなプログラムもエラーが出ます。

#include <stdio.h>

main()
{
    printf("Hello!\n";
}

この例では、printf関数のカッコを閉じ忘れています。 このような些細な間違いも、エラーとして表示されます。

このような些細なつづり間違いや文法的に間違ったプログラムは、 うまくコンパイル(翻訳)できずにエラーを表示して終わってしまいます。

Cのプログラムを書く場合は、C言語の文法に厳密に従い、 つづり間違いもおかさないようにする必要があります。 (もちろん、これはプログラミング言語一般に言えることですが)


例題1

注意:こちらの問題は、プログラミング演習1で同様の内容を学習済みのはずです。

次のフィボナッチ数列とよばれる数列 {Fn} を考える。

F1 = F2 = 1, Fn+1 = Fn + Fn-1, n≧2.

この数列は、

1, 1, 2, 3, 5, 8, 13, 21, 34, ......

と続く数列である。

F1 から F40 を画面に表示するプログラムを作成せよ。

また、隣り合うフィボナッチ数の比、すなわち

Fn/Fn+1

も画面に表示せよ。

ちなみに、フィボナッチ数の比が n が大きくなるにつれて、黄金比、

(-1 + √5)/2 = 0.618033988749....

に近づくことが知られている。

(雑談)フィボナッチ数は、上記の様に一見大変人工的なものですが、 実はフィボナッチ数や黄金比は自然界によく見られます。 ひまわりの種の配置がフィボナッチ数や黄金比に関係があると言うことは良く知られているし、 ギリシャの神殿に黄金比が見られると言った話もあります。 人工的なものはともかく、自然界になぜフィボナッチ数や黄金比が良く見られるのかは 自然科学の一つのテーマとして面白いです。 Wikipediaなどで気軽に調べはじめることができるので、様々な事に興味を持ってみてください。 関連する書籍も多数あるので、図書館に行って調べてみてはどうでしょうか。


コメントと字下げ

コメント

C言語では、ソースプログラムを読みやすくするために、 コメントを書き込むことができます。 /* と */ で囲まれた部分(行をまたいでもOK)、 あるいは// 以降に書かれた部分(行末まで)がコメントと認識され、 コメントに書かれていることはコンパイルに影響ありません。 例えば、前回の例題ソースプログラムにコメントを書き込みましょう。

/* ex01 */
/* ev22222222 小田切健太   */
#include <stdio.h>
/* main 関数 */
int main()
{
  /* Hello! と画面に表示する */
  printf("Hello!\n");
  return 0;
}

このような短いプログラムではあまりコメントのありがたみは分かりませんが、より長いプログラムでは大変効果的です。 プログラムのこの部分ではどのような処理をしているかといった内容を コメントとして書き入れる癖をつけておきましょう。

また、プログラムの一部を一時的に実行させたくない場合にも利用できます。 これは、プログラムの動作がおかしい時に問題の発生箇所を特定する(バグ取り)のに有用です。

注意:
全角半角に気をつけてください。 日本語が打てる状態で * や / を書くと全角文字になってしまう場合があります。 空白文字も同様に全角の空白文字となってしまうことがあります。 こまめに日本語モードと英数モードを切り替えてください。 このようなミスもコンパイル時にエラーとなります。 例えば * / a B は半角文字ですが、 * / a B は全角文字です。 つづりも文法もあっているのにエラーが出る場合、 まずは全角文字を使っていないか疑いましょう。

字下げ

サンプルソースプログラムでは適当に字下げが行われていました。 実は、コンパイル時には字下げは全て無視されます (コンパイラにとっては字下げなど不要)。 字下げは、人間がソースプログラムを読みやすくするために行うもので、 今後より複雑なプログラムを作成する際には見やすいように字下げをしてください。 字下げのスタイルは色々ありますので、自分が一番わかりやすい方法で行えば良いでしょう。


変数

全てのプログラミング言語には変数と呼ばれるものがあります。 変数は、データ(数値であったり、文字列であったり)を一時的に保存しておく箱によくたとえられます。 コンピュータプログラムは、そのような変数と呼ばれる箱に入ったデータを処理することにより、有用な仕事をします。 また、変数を用いることで、より柔軟な処理を行うことができます。 変数は、使い方を分かってしまえばなんてことも無いのですが、もしも分かりにくい人はどんどん質問してください。

変数には、整数型実数型文字型と数種類ありますが、まずは整数型変数を扱います。 整数型変数は、整数値を保存するための箱です(実数値は保存できない)。

変数にはそれぞれ符号付型(signed)と符号無し型(unsigned)の二種類がありますが、当面符号付型を用います(負の数も扱いたいので)。


printf 関数を使って変数の内容を画面に表示する

ここでの目標は、整数型変数の内容を画面に表示する場合の printf 関数の使い方をマスターすることです。

/* ex01 */
#include <stdio.h>
int main()
{
  /* 整数型の変数 a, b, c を用意する */
  int  a, b, c;
  /* 整数型変数 a に値 5 を代入 */
  a = 5;
  /* 整数型変数 b に値 12 を代入 */
  b = 12;

  /* 整数型変数 c に a+b の結果を代入 */
  c = a + b;
  /* 変数 c の内容を画面に表示 */
  printf("%d\n", c);

  return 0;
}
 

整数値を画面に表示するにはこの例のように

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

とします。 \n は前半にでてきた改行を示すものでした(これを特殊文字と呼びます)。

問題は %d です。 printf 関数の中で用いられる %d のことを、変換文字列といいます。 %d の d は decimal(10進数)の頭文字の d に由来します。 つまり、 %d は対応する変数または数値を10進数整数として表示する働きをもちます。 対応する変数または数値とは、今の場合は整数型変数 c ということになります。

複数の数値を表示したい場合は、次のように書くこともできます。

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

この場合、%d が3つ並んでいますが、これらはそれぞれ後に続く a, b, c に対応しています。

先の例題プログラム(ex02)を次のように変更してみましょう。 printf 関数のところだけ変更されています。

/* ex02 */
#include <stdio.h>
int main()
{
  /* 整数型の変数 a, b, c を用意する */
  int  a, b, c;
  /* 整数型変数 a に値 5 を代入 */
  a = 5;
  /* 整数型変数 b に値 12 を代入 */
  b = 12;

  /* 整数型変数 c に a+b の結果を代入 */
  c = a+b;
  /* 変数 a,b,c の内容を画面に表示 */
  printf("a=%d\nb=%d\na+b=%d\n", a, b, c);

  return 0;
} 

この場合も3つの %d があり、それぞれ後ろの a, b, c に対応しています。 つまり、変換文字列と printf 関数の第2引数以降の引数の数は一致しています。

 先の例でもありましたが、対応するものは変数である必要はありません。 次のような使い方も可能です。

printf("%d\n", 4+5);

この場合、9と表示されます。

代表的な変換文字列を示しておきます。

%o 8進数で出力する
%d 10進数で出力する
%x  %X 16進数で出力する.%x は小文字,%Xは大文字で出力
%f %lf 浮動小数点数として出力する (%lf は倍精度浮動小数点時・後述)
%c 文字として出力する
%s 文字列として出力する

このような変換文字列を、変数の型や表示したい様式にあわせて使うことになります。


実数型変数

実数型変数は 

float  u, v, w;

というように宣言します。 float は、浮動小数点数の(floating point number)からきています。 また、 printf 関数で実数型変数を表示する場合の変換文字列は %f です。

/* ex03 */
#include <stdio.h>
int main()
{
  float  pi, r, s;

  pi = 3.14159;
  r = 2.0;

  s = pi*r*r;

  printf("半径 %f の円の面積は %f です\n", r, s);

  return 0;
}

このプログラムでは、実数型変数 pi, r, s を用意し、pi には 3.14159 という値を代入、r には 2.0 という値を代入しています。 さらに、 s に pi*r*r (円の半径を r とした場合の円の面積を求める公式)の計算結果を代入しています。 つまり、最後の printf 関数で画面に表示される s の値は、半径 2 の円の面積(の近似値)ということになります。

注意:
C言語の実数型(浮動小数点数型)には float (単精度浮動小数点数型)のほかに double (倍精度浮動小数点数型)があります。 名前からわかるように、倍精度浮動小数点数型の方が、より高精度に数値を表現することができます (通常単精度浮動小数点型は32bitであって、倍精度浮動小数点型は64bitで表現される)。 現在のマシンでは、 float を用いる利点はほとんど無いため、今後は実数型変数として double を用いることにします。 倍精度浮動小数点数型 double を用いる場合、変換文字列として%lfを用いる必要があります。

先のサンプルプログラムの倍精度浮動小数点数型を用いたバージョンを以下に示します。 (ここでは printf 関数のみ使っているので、実際には printf 関数内の変換文字列は %f, %lf 双方どちらでもよい(C99の場合)。 後にでてくる scanf 関数の場合には double 型を用いる場合には %lf を用いる必要がある。)

/* ex04 */
#include <stdio.h>
int main()
{
  double  pi, r, s;

  pi = 3.14159;
  r = 2.0;

  s = pi*r*r;

  printf("半径 %lf の円の面積は %lf です\n", r, s);

  return 0;
}

 


実数型変数を使うことで、円の面積(の近似値)を求めることができました。 しかし、これでは違う半径の円の面積を求めたい場合、いちいちエディターでソースプログラムファイルを変更して、コンパイルし実行せねばなりません。 半径はキーボードで我々が入力したいものです。 次節では、標準関数である scanf 関数を使って、キーボードからの数値データの入力を実現します。


scanf 関数

 キーボードから数値データを入力するには、 scanf 関数という標準関数を用います。 printf 関数は出力を行う関数でしたが、scanf 関数は入力を行う関数です。

scanf 関数による整数値の入力

 scanf 関数を用いたもっとも簡単なプログラムは次のようなものになります。

1: #include <stdio.h>
2: 
3: int main()
4: {
5:     int i;
6: 
7:     scanf("%d", &i);
8:     printf("%d\n", i);
9:     return 0;
10: }

このプログラムは、キーボードから入力された整数値データをそのまま画面に表示します。 このプログラムの中で、7行目の scanf 関数がキーボードからのデータ入力を行います。

scanf("%d", &i);

となっていますが、前半のダブルクォーテーションで囲まれた文字列は、printf 関数と同様の変換文字列です。 ただし、printf 関数のように通常の文字列と変換文字列が両方書くことはできず、 scanf 関数では変換文字列のみ書くことができます。 このサンプルプログラムでは、整数型変数 i に値を入力するので、整数型変数の変換文字列である %d がかかれています。 そのあとには、コンマ に続いて &i と書かれています。 scanf 関数では、変換文字列に続いて入力されたデータを格納する変数名がきますが、変数名の頭に & をつける必要があります。 & をつけねばならない理由は、ポインタに関係することを思い出してください。 ポインタについては改めておさらいしますので、ここではひとまず、 scanf 関数では変数の頭に必ず & をつけると再確認してください。

scanf 関数による実数値の入力

 scanf 関数を用いて実数値を入力し、それをそのまま出力するサンプルプログラムは次のようになります。

1: #include <stdio.h>
2: 
3: int main()
4: {
5:     double a;
6: 
7:     scanf("%lf", &a);
8:     printf("%lf\n", a);
9:     return 0;
10: }

整数が実数に変わっただけなので、特に問題ないですね。

入力促進メッセージの出力

 先の2つのプログラムでは、「・・を入力してください」といったメッセージは全く画面にでませんでした。 これでは、プログラムを使う人に対してあまりに不親切です。 そこで、先ほどの例を次のように変更しましょう。

 1: #include <stdio.h>
 2: 
 3:int main()
 4: {
 5:     double a;
 6: 
 7:     printf("実数値を入力してください: ");
 8:     scanf("%lf", &a);
 9:     printf("入力された実数値は %lf です\n", a);
10:     return 0;
11: }

7行目に printf 文が加わりました(9行目も変更されています)。 このように、入力促進メッセージを表示する場合には、scanf 関数に先立って printf 関数で表示する必要があります。 次のように書きたくなるかもしれませんが、このような書き方はできません

間違った例

scanf("実数値を入力してください: %lf", &a);

円の面積を求めるプログラム(改良版)

 では、scanf 関数を使って、先ほど作成した円の面積を求めるプログラムを改良しましょう。

 次のサンプルプログラム打ち込んでください(行番号は無視してください)。 これまでの知識で十分理解できるはずです。



このサンプルプログラムは、先ほどのサンプルプログラム ex04 を少しだけ変更した内容となっています。 先のプログラムで 、

r = 2.0;

としていた部分が、printf 関数と scanf 関数に置き換わっています。 ターミナルでコンパイルし実行すると、

円の半径を入力してください:

と表示され、その状態でとまります。 そこで、メッセージどおりに面積を求めたい円の半径を入力します。 例えば、3.2 と打ち込んで、Enter キーを押してみましょう。 半径 3.2 の円の面積が求まったでしょうか?

 この節で学んだことをまとめると、

printf 関数と scanf 関数を組み合わせて用いれば、メッセージを表示して、 キーボードから数値を変数に読み込むことができる

ということになります。


if文、for文、関数については、プログラミング演習1で学習済みなので、 復習が必要な人はそちらも確認してください。

条件分岐・if文

以下の最大値を表示するサンプルプログラムを確認して、if文について思い出してください。

// 注:a,b,c で互いに違う値の場合のみ正しく動きます
// 余裕がある人は、等しいものがある場合の修正の仕方も考えてみてください

#include <stdio.h>

int main()
{
  int a = 1;
  int b = 3;
  int c = 5;

  if(a < b){
    if(b < c){
      printf("cが最も大きい\n");
    }
    else{
      printf("bが最も大きい\n");
    }
  }
  else if(a < c){
    printf("cが最も大きい\n");
  }
  else{
    printf("aが最も大きい\n");
  }

}

繰り返し文・for文、while文

以下のべき乗の計算をする関数のサンプルプログラムを確認して、 繰り返し文について思い出してください。

// a の b 乗を計算する関数

int beki(int a, int b){
  int i;
  int j = 1;

  for(i = 1; i <= b; i++){
    j = j*a;
  }
  return j;
}

配列変数の宣言と利用

C言語に限らず、ほとんどのプログラミング言語には配列変数と呼ばれるものがあります。 配列変数は、例えば次のように宣言することで用意できます。 (配列(1次元配列)については、プログラミング演習1で学習済み。)

int i[5];

この宣言により、次のように整数型の変数が5個用意されることになります。

i[0], i[1], i[2], i[3], i[4]

かぎ括弧 [ ] で囲まれた数字は、添字と呼びます。 このように、添字は 0 から始まるので注意が必要です。 このプログラムは、1,2,3いずれかの数字を打ち込み、それぞれ何回入力されたかを数えるプログラムです。 0 を入力することで、入力を終えることができるようになっています。 (途中 if 文の入れ子があります。 入れ子構造を分かりやすくするために字下げが行われていることに注意してください。 字下げによって、構造がよくわかるはずです。)

以下のサンプルプログラム(1〜3の数字が何回入力されたかを表示するプログラム)を確認して、 配列の使い方について思い出してください。

#include <stdio.h>
int main()
{
  /* 整数型変数の宣言 */
  int i, n, c[3];

  /* 整数型配列の初期化を行う繰り返し文 */
  for (i = 0; i < 3; i++) {
    c[i] = 0;
  }

  /* n の初期化(while文の条件式をはじめから満たさないようにするため) */
  n = -1;

  /* 0 が入力されるまで入力を受けつづける繰り返し文 */
  while (n != 0) {
    printf("1, 2, 3 いずれかを入力してください(0 で入力終わり): ");
    scanf("%d", &n);

    /* 入力された n の妥当性をチェック */
    if ((n <=0) || (n > 3)){
      if(n == 0) {
        /* n が 0 だった場合 */
        printf("0 が入力されました。入力を終わります。\n");
      }
      else {
        /* n の範囲が不当だった場合 */
        printf("1, 2, 3 以外の数値が入力されました。無視します。\n");
      }
    }
    else {
      /* n が 1,2,3であった場合、c[n-1] の値を一つ増やす */
      /* n-1 となっているのは、添え字が 0 から始まることによる */
      c[n-1]++;
    }
  }

  /* 結果を表示する繰り返し文 */
  for (i = 0; i < 3; i++) {
    printf("%d が %d 回入力されました。\n", i+1, c[i]);
  }
  return 0;
}

このように、配列変数の添字には整数型の変数が使えます。

整数型の配列以外に、実数型の配列も作ることができます。

double a[6];

と宣言することにより、

a[0], a[1], a[2], a[3], a[4], a[5]

と、5つの実数型変数を作ることができます。


2つの変数の数値データの入れ替え

2つの変数に記憶されている数値データを入れ替えたい場合があります。 その場合には、入れ替えを補助する余分な変数を一つ用意する必要があります。 次のプログラムは、実数型変数の内容を入れ替えるプログラムです。

#include <stdio.h>
int main() {
  double a, b, tmp;

  a = 1.2;
  b = 5.3;

  /*  入れ替え前の状態を表示 */
  printf("a = %lf, b = %lf\n", a, b);

  /* 補助変数 tmp に a の内容を退避 */
  tmp = a;
  /* a に b の内容を代入 */
  a = b;
  /* b に退避しておいた a の内容を代入 */
  b = tmp;

  /* 入れ替え後の状態を表示 */
  printf("a = %lf, b = %lf\n", a, b);

  return 0;
}


2次元配列については、次の様に宣言し利用できます。

double u[100][200];

(プログラミング演習1で学習済み。 また後日、詳しくおさらいします)


これまでの内容の確認として、次の課題に取り組んでください。

◎課題1

プログラムの利用者が自由に 0 以上の整数値 a, b を入力できるように scanf 関数をつかったプログラムを作成せよ。
さらに、足し算だけでなく、その他 a-b, a*b, a/b, a の b 乗 の結果を全て表示するようにせよ。
また、以下の小問1, 2, 3 について答えよ。

  1. b を 0 としてみよ。その場合 0 での割り算が生じ不都合がでる。この問題を回避できるようにプログラムを改良せよ。(条件分岐が必要)
  2. a = 1, b = 2 としたとき a/b の結果は 0 となる。試した上で、その理由を答えよ。(理由はプログラム内にコメントとして記載してください)
  3. 結果が 0.5 となるようにするには、どうすれば良いか。改良したプログラムもあわせて作成せよ。

ヒント:変数 a, b の型に注目。変数の値に 0.5 を代入するためには、型をどうすれば良いか?

◎課題2(以下の(1),(2)のどちらかを選択して回答)

(1)

うるう年は「4で割り切れ、かつ100で割り切れない年」、あるいは 「400で割り切れる年」どちらかに当てはまる年(西暦)のことである。 scanf関数を用いて年(西暦)を入力させ、入力された年がうるう年かどうかを判定し、画面に結果を表示するC言語のプログラムを作成せよ。

余力がある人向け

入力された年(西暦)が和暦だと何年になるかを画面上に表示する機能を追加せよ。全ての和暦だと大変なので、明治、大正、昭和、平成、令和が表示できれば良い。


(2) こっちの方がちょっと難しい

乱数(でたらめな数値)を使って円周率を求めるプログラムを作成せよ。
プログラム演習1では、以下の円の面積を利用した方法を用いたが、 ここでは球の体積を用いた方法で円周率の近似値を求めよ。
また、乱数の数を変更した場合、近似値がどのように変化するのかについて調べよ。
さらに、円の面積で求めた場合と球の体積で求めた場合の違いについて、考察せよ。
例えば、同じ数の乱数を使って求めた場合、円の面積から求めた値と球の体積から求めた値のどちらがより正確な値に近いか。

乱数は rand() 関数を用いることで得られる(正確には、擬似乱数が得られる)。 rand() 関数で得られるのは、0 〜 RAND_MAX の数値で、RAND_MAX はシステムによって異なる可能性がある。 また、rand() 関数を利用するには stdlib.h をインクルードする必要がある。

次に簡単な乱数を表示するプログラムを示す。

#include <stdio.h>
#include <stdlib.h>

main()
{
  int i;
  for(i = 0; i < 10; i++)
  {
    printf("%lf\n", rand()/(double)RAND_MAX);
  }
}

このプログラムでは10個の乱数を表示するが、それは 0〜1 の実数値(実際には有理数)をもつ一様乱数である。

この乱数を用いて、円の面積から円周率の近似値を以下の方法で求められる。

  1. 一辺の長さが1の正方形領域内(0<=x<=1 & 0<=y<=1)に、上記の乱数を使って x 座標と y 座標を取得し、でたらめに点を打つ。
  2. でたらめに打った点が、(0,0)を中心とする半径 1 の円の中に入っているかを判定する。
  3. でたらめに打った点が円の中に入っていた確率を計算する。
  4. でたらめに打った点が円の中に入る確率は、理論上 pi / 4 である(面積を考えれば明らか)ことより、計算した確率から近似的に円周率を求める。

上記の方法は、モンテカルロ法と呼ばれている。 (Wikipediaのモンテカルロ法のページに この計算を描画している画像あり)

ちなみに、モンテカルロ法は積分の計算(数値積分)に利用することができる。 言うまでもないが、円の面積の公式も球の体積の公式も、本来は積分によって求められる。

◎課題3

5つの整数を入力すると、まずはそれらを入力順に表示し、その後小さい順に並べ替え、小さい順に表示するプログラムを作成したい。 次のプログラムは未完成であるが、dat[0]〜dat[4] の内容を小さい順に並べ替える部分を追加し、 プログラムを完成させよ。 また、プログラムを追加した部分でどの様な処理をしているのかについて、 コメント文で詳細に説明すること。 (変数 j, tmp は未完成のプログラム内では宣言はしてるが使っていない。 並び替え時に必要であればそれらをつかっても良い。 また、さらに変数が必要であれば、用意してもかまわない。)



参考:
データの並び替え作業のことを、ソートと呼ぶ (ソートについては、プログラミング演習1も参照)。 ソートはプログラム内で頻繁に用いられることもあり、様々なアルゴリズムが考案されてきた。 代表的なソートのアルゴリズムとして、バブルソート、ヒープソート、クイックソート等が挙げられる (ちなみに、プログラム演習1でやったのはバブルソート。一番シンプルだが、効率は悪い)。 もし自分で頑張って考えてみても、どうやって並び替えたらよいかのアルゴリズムが思い浮かばなければ、 上記のアルゴリズムについて調べてみると良いだろう。 ただし、 意味を理解せずに拾ってきたプログラムを単にコピぺするのは厳禁。 カンニングと同じ扱いとして、大幅に減点する。 きちんと自分なりに理解して、 詳細な説明をコメント文として加えること。

ちなみに、ピタゴラスイッチで出てくる「しめじソート」はマージソート、 「ジャガイモソート」はクイックソートである。 ソートアルゴリズムを視覚化した動画を見ると、 各アルゴリズムによるソート過程の違いが視覚的にわかって面白い (音も面白いが、授業中に見る場合は注意)。


課題の提出

「課題1および課題2および課題3」において作成したプログラムを提出してください。(課題2は(1)と(2)のどちらかを選択)
ファイル名は、課題1は 01-rep1.c、課題2は 01-rep2.c、課題3は 01-rep3.cとしてください。
遅れると提出できない設定にしているので、必ず時間内に提出すること。

ただし、コメント文として

学生番号、名前 を書き込むこと。

また、提出したファイルが正しいものであるかどうかは、毎回確認するよう習慣づけてください。

提出締め切り: 2024年9月23日19時00分


戻る