すべての作業は、/home/*gradeの下の自分の名前ディレクトリの下で必ず行うようにして下さい。
守らないと同じマシンを使っている他の人すべてに迷惑がかかるので注意して下さい。
C言語を使ってみよう。
Geant4が書かれている言語はC++ですが、C++はC言語と共通することが多いので、まずはC言語の基本を習得してください。
第1節 Hello World!
まず,emacsを使って以下のように,”101.c”というテキストファイルを開いてみましょう.
$
emacs 101.c &
emacsが起動したら,以下のプログラムを入力します。
#include <stdio.h>
main(){
printf("Hello World!!\n");
}
入力が終了したら,このテキストファイル(ソースファイルという)をコンパイルします。
コンピューターは,このソースファイルのままでは何をすべきか全く理解できません。
今あなたが入力した内容をコンピューターに理解してもらうためには,”コンパイル”というステップを踏む必要があります。
早速コンパイルしてみましょう。
emacsを終了させて,以下のように入力します。
$
g++ 101.c -o 101.out
何も表示されずに,入力待ちの状態になりましたか?
なにやら意味不明なメッセージが表示されている場合は,あなたの入力した(コンピューターに命令した)言葉に文法的間違いがあり,コンパイラが理解できない状態になっています。
エラーメッセージの表示がなく、”101.out”というファイルが作成されていれば、コンパイル成功です。
101.outというファイルは、”実行可能ファイル”呼ばれるもので、あとはこのファイル名をそのまま入力すれば、101.outというプログラムが実行されます。
$
101.out
Hello World!!
よくありがちな”Hello World!!”です。
プログラムの解説です。
#include <stdio.h>
は,ヘッダファイルを読み込んでいます。
ヘッダファイルには,プログラムで使われる関数(Cでは命令は全て関数として扱われる)一式についての記述がなされています。
一番最初に必要なヘッダファイルを読み込まないと,コンパイラは関数に関する情報がないため,これ以降のプログラムを解読できなくなります。
Cを書くときのおまじないとでも思っておけばいいです。
main(){
...
}
この{から}に囲まれた部分がメイン関数,つまりプログラムの本体です。
今のプログラムの場合、
printf("...");
は文字をモニターに出力(標準出力)する関数で,”...”の中が表示させる内容です.
また,\nは改行することを表します。
課題1
第2節 文字の入出力
文字の入出力は,プログラミングにおいて必ず必要となる基本的な事柄です。
ここでは,数値や文字列の入出力および,それにともなう変数の宣言について学びます.以下のプログラムをみて下さい。
#include <stdio.h>
main(){
int i;
double d;
char c[64];
printf("int = ");
scanf("%d",&i);
printf("double = ");
scanf("%lf",&d);
printf("char = ");
scanf("%s",c);
printf("i = %d\n",i);
printf("d = %lf\n",d);
printf("c = %s\n",c);
}
プログラムを実行し、以下のように値を入力すると、
int = 1
double = 1.3
char = abc
それらの値が標準出力に出力されます。
i = 1
d = 1.300000
c = abc
プログラムの解説
int ...;
C言語で変数(値をしまっておく箱とでも考えればよい)を使う際には,どのような種類(変数型)で何と言う名前の変数を使うかを前もって宣言しなければなりません。
int i;は、”整数型(int型)で、iと言う名前の変数をこれから使います”ということを宣言しています。
double ...;
倍精度実数型の変数の使用を宣言しています.
有効桁数が15桁,およそ10
-308から10
308の範囲で使用できます。
char ...;
文字型といい,上の例だと,”63文字分格納できるcという名前の文字型変数を使います”という意味になります。
なぜ64文字分ではなく63文字分であるかというと,C言語の約束により文字列の最後に\0というキャラクターコードが付加されるからです。
実際には,必要な文字数よりも多少余裕を持った長さを確保します.
scanf(...);
キーボードからの入力(標準入力)を変数に渡す関数です。
”%d”は,”int型変数を10進数で受け取ります”の意味で(書式指定列という)、受け取った値は、iに格納されます。
このとき注意しなければいけないのは、
格納する変数名の頭に&”をつけることです。
これは,変数iのアドレスをscanf関数に渡すためです。
%lfは倍精度実数型、%sは文字型のデータを受け取ることを示しています。
また、
文字型に限っては”&”をつけません。
これは,文字型の実体が配列変数(後で説明)のため,先頭アドレスをscanf関数に渡せばいいからです。
printf(...);
”%d、%lf、%s”は、表示するデータの種類を示していて、意味はscanf関数と同じです。
アウトプットを見るとわかるように,”%d、%lf、%s”の部分に,後に続く変数の中身が表示されるわけです。
また、
printf関数では,表示させたい変数の頭に”&”はつけないので,scanf関数と混乱しないように注意しましょう。
(参考) データ型や書式指定列は,ここにあげた例以外にもさまざまなものがあるので,C言語の本などを必要に応じて参考にして下さい.
課題2
第3節 繰り返し
1から10までの和を求めるプログラムを以下に3パターン示します。
例1
#include <stdio.h>
main(){
int i,sum;
sum=0;
for(i=1; i<=10; i++){
sum=sum+i;
}
printf("sum = %d\n",sum);
}
-------------------------------------------
例2
#include <stdio.h>
main(){
int i,sum;
i=1;
sum=0;
while(i<=10){
sum=sum+i;
i++;
}
printf("sum = %d\n",sum);
}
--------------------------------------------
例3
#include <stdio.h>
main(){
int i,sum;
i=1;
sum=0;
do{
sum=sum+i;
i++;
}while(i<=10);
printf("sum = %d\n",sum);
}
これらの実行結果は、
sum = 55
で,みんな同じです。
では、C言語にはなぜ繰り返しに3種類もの異なる関数が用意されているのかというと、それぞれをうまく使い分けることによって、簡潔でエレガントなソースファイルが記述できるからです。
プログラムの解説
for(...){...}
for文は指定回数反復処理といいます。例1の場合,”iを1からスタートし(i=1;)、iが10以下の間(i<=10;)は{...}の内容を実行しなさい。
また、{...}の内容を実行し終えるたびに、iを1増やしなさい(i++)”という意味です。
i++は、インクリメントといってC言語に特徴的な表記の仕方の一つです。
これは、i=i+1と同じ意味で、i++をi=i+1と書いても全く問題はありません。
while(...){...}
while文は不定回数のループです。
ループ継続の条件を式のはじめに判定するため、
条件によってはwhile文以降のループの内容を一度も実行しないこともあります。
ループ継続の条件を(...)の中に、ループさせる内容を{...}に記述します。
do{...}(...);
do-while文は同じく不定回数のループですが,ループ継続条件を式の最後に判定するため、
少なくとも一回はループの内容が実行されます。
{...}にループさせる内容を、(...)にループ継続条件を記述します。
(参考) 和を計算するために”sum=sum+i”と記述してありますが、この他にもさまざまな演算子がC言語には用意されています。
たとえば,積を計算するならば”sum=sum*i”と書きます。
必要に応じて,各自でどのような演算子があるかを調べておきましょう(四則演算の演算子ぐらいは調べましょう。)。
課題3
第4節 配列変数
配列変数とは,簡単に言えば番号のついた変数のことです。
変数に番号をつけることで,多くのデータをその番号で処理できるようになります。
課題3−1は、積を求めるだけのプログラムでしたが、例えば、後で6番目のデータと9番目のデータの平均を計算したいなんてときはどうすればいいでしょうか?
そんなときは配列変数を用いると便利です。
下のプログラムは,入力された10個のデータの和と平均,および,最初と最後のデータの平均を求めるものです。
#include <stdio.h>
main(){
int i;
double d[10],sum,ave;
sum=0;
for(i=0; i<10; i++){
printf("data %d = ",i);
scanf("%lf",&d[i]);
sum=sum+d[i];
}
ave=sum/10;
printf("sum = %lf\n",sum);
printf("ave = %lf\n",ave);
sum=d[0]+d[9];
ave=sum/2;
printf("ave of #0 and #9 = %lf\n",ave);
}
入力
data 0 = 1
data 1 = 1
data 2 = 1
data 3 = 1
data 4 = 1
data 5 = 1
data 6 = 1
data 7 = 1
data 8 = 1
data 9 = 1
出力
sum = 10.000000
ave = 1.000000
ave of #0 and #9 = 1.000000
プログラムの解説
double d[10],...
このd[10]というのが倍精度実数型の配列変数を宣言してるところで、”dという名前の箱を10個用意してください”という意味になります。
箱の番号は0番から9番までなので注意して下さい。
それに伴って、for文のiの値も0からスタートしています。
また、ループ継続条件を、”i<10;”のように”iが10未満”と表現することも可能です。
課題4
第5節 条件文
条件によってプログラムの流れをかえることが必要なときは,if文を使います。
以下のプログラムをみて下さい。
#include <stdio.h>
#include <stdlib.h>
main(){
int a,b;
printf("a = ");
scanf("%d",&a);
printf("b = ");
scanf("%d",&b);
if(a<b){
printf("a is less than b.\n");
exit(1);
}
else if(a>b){
printf("a is greater than b\n");
exit(1);
}
else if(a==b){
printf("a is equal to b.\n");
exit(1);
}
}
入力
a = 1
b = 2
出力
a is less than b.
入力
a = 2
b = 1
出力
a is greater than b.
入力
a = 1
b = 1
出力
a is equal to b.
プログラムの解説
if(...){...}else if{...}
例題のように”もし...ならば...”の条件分岐がif文の働きです。
多分岐のときは、”else if”で何個でも場合わけできます。二分岐のときは特に、
if(...){
...
}
else{
...
}
のようにまとめるのが普通です。
exit(1);
これは、プログラムを終了させる関数です。”()”の中の数値は、プログラム終了時にシェルに与える戻り値です。
たいていの場合は1です。また、この関数を使用するにはヘッダファイル”stdlib.h”をインクルードします。
(参考) 条件を示す論理演算には,上にあげた例以外にさまざまなものがあるので,各自で必要に応じて調べて下さい。
また,数値による場合わけにはswitch文を用いることもあります。
課題5
第6節 ファイルのオープンとクローズ
C言語でのファイルのオープン、クローズの方法を見ていきましょう。
以下のプログラムは、入力された10個のデータを”data.dat”というファイルに出力するプログラムです。
#include <stdio.h>
#include <stdlib.h>
main(){
FILE *fp;
int i;
double d[10];
if((fp=fopen("data.dat","w"))==NULL){
printf("error\n");
exit(1);
}
for(i=0; i<10; i++){
printf("data %d = ",i);
scanf("%lf",&d[i]);
fprintf(fp,"%lf\n",d[i]);
}
fclose(fp);
}
入力
data 0 = 0
data 1 = 1
data 2 = 2
data 3 = 3
data 4 = 4
data 5 = 5
data 6 = 6
data 7 = 7
data 8 = 8
data 9 = 9
プログラムを実行して、適当な値を入力し終えると、”data.dat”というファイルが今いるディレクトリ(カレントディレクトリ)にできているます。
このファイルの中身をみるには”cat”コマンドを用います。
”data.dat”の中身をチェックしてみましょう。
$
cat data.dat
出力
0.000000
1.000000
2.000000
3.000000
4.000000
5.000000
6.000000
7.000000
8.000000
9.000000
倍精度実数型でデータが保存されているのがわかります。
プログラムの解説
FILE *fp;
ファイルポインタの宣言文です。
”*”はポインタであることを示しています。
わかりやすく説明すれば、ファイル”data.dat”をこれからプログラム内で扱うにあたって、プログラム側とフロッピーまたはハードディスクなどの補助記憶装置側との窓口を設けているようなものです。
fp=fopen("data.dat","w")
この部分が、実際にファイルをオープンしているところです。
”fpという窓口にファイル名 data.datをつないで欲しい”という意味です。
また、”w”は”write”の頭文字で、”ファイルに書き込む”ということを示しています。
例題では、fopen関数はif文に埋もれていますが,単純に
fp=fopen("data.dat"."w");
としても、問題なくコンパイルされます。
ではなぜ、例題のような複雑な形にするのでしょうか?
答えは、”もしファイルオープンに何らかの原因で失敗した際のエラー処理”が必要だからです。
fopen関数は、ファイルオープンに失敗すると、NULL文字というものを返してきます。
つまり、if文の中身は、”fpの値がNULLだったら”という条件文になっています。
そして、実際にファイルオープンに失敗した際は、メッセージを出力し、プログラムを終了させる仕組みになっています。
C言語は、ポインタを駆使することで、ときにはマシン語のようなきめ細かい、そしてプリミティブな操作が可能です。
このことは、誤動作を起こした際に、最悪OS全体をフリーズさせるような結果を招く可能性を示唆しています。
つまり、安全のために、プログラマーは、予測できるエラーはあらかじめ責任を持って処理できるようにプログラムを設計することが必要とされます。
ファイルをオープンする際の定石ですから覚えておきましょう。
fprintf(...);
printf関数に似ていますが、頭に”f”がついています。
printf関数は、出力先が標準出力(モニター)なのに対して、fprintf関数は出力先(窓口)を指定できます。
今の例の場合,それは”fp”です。
あとは、printf関数と一緒です。
fclose(fp);
開いた窓口”fp”を閉めています。
では,書き込んだファイルをどのように読み込んだらいいでしょうか?
以下のプログラムを見て下さい。
#include <stdio.h>
#include <stdlib.h>
main(){
FILE *fp;
int i;
double d[10];
if((fp=fopen("data.dat","r"))==NULL){
printf("error\n");
exit(1);
}
for(i=0; i<10; i++){
fscanf(fp,"%lf",&d[i]);
printf("%lf\n",d[i]);
}
fclose(fp);
}
出力
0.000000
1.000000
2.000000
3.000000
4.000000
5.000000
6.000000
7.000000
8.000000
9.000000
プログラムの解説
fp=fopen("data.dat","r");
さっきと違うのは,”w”が”r”になっているとこです。
rは、”read”の頭文字で読み込みを表しています。
fscanf(...);
これも、scanf関数に似ていますが、違いは入力先を指定できることです。
ちなみに、scanf関数は入力先は標準入力(キーボード)でしたね。
後の書式は、scanf関数と同様です。
(参考) fprintf、fscanf関数以外にもファイル操作似関する入出力関数はたくさんあるので、その場に応じて適切な関数を使うようにして下さい。
課題6