C言語の基礎Ⅱ |
さて、話を元に戻してさっそく次の段階に進みましょう。いままでは本当の基礎でしたが、この章は大学の課題をこなす程度に重要な内容を含んでいます。
関数を作る |
関数とは、大きな計算処理を小さな仕事に分割するための構造です(似たようなものにサブルーチンという用語があります)。C言語での関数は、数学の世界で定義されるように「いくつかの値をもらって1つの値を返す」ものです。数学ではそれが数式で表現されますが、C言語では文(命令)の集まりで表現されます。
関数は、使う前に定義をしなければなりません(本当はそうしなくてもいいのだが、今はこう覚える)。つまり、main()の前に書きます。関数の定義の書式は次のようになります。
返値の型 関数の名前(引数の宣言) { 宣言と文 }
関数の名前には、変数の名前と同じ規則が適用されます(「変数を使う」の節を参照)。
返値は、どの型でも自由に使えます。返値が必要ない場合は、void型を指定します。省力するとvoid型が適用されます。返値の(値の)指定にはreturn文を使います。
#include<stdio.h>
/* ただの足し算をする関数plus() */ /* 数学で言うところの関数のように使える */
int plus(int a, int b)
{
return a + b; /* aとbをもらって、その和を返す */
}
/* printf()を呼ぶだけの関数func() */ /* 返値も引数もなし */
void func() {
printf("関数func()の中です。\n"); /* 返値が無いから、return文は不要。(あっても問題ない)
*/
} /* これはいつものメイン関数。これも関数なのです */
int main()
{
int a, b, c;
a = 5;
b = 2;
c = plus(5, 2);
printf("5 + 2 = %d\n", c); /* 上の例は変数cが無駄。下の例は簡潔でよい */
printf("5 + 2 = %d\n", plus(a, b));
func();
return 0;
}
return文が実行されると、その時点で呼び出しもとの関数に実行が戻ります(main()関数ならプログラムが終了します)。return文は関数内にいくつあってもよいのですが、返値がvoidでない関数には必ず1つ以上のreturn文が必要です。次にreturn文の書式を示します。
return式; ※返値がvoidの場合、式は必要ない
余談ですが、printf()やscanf()も関数です。C言語には、標準の関数が用意されていて、多くの処理はそれらの関数を用いることによって実現されます。
変数のスコープ |
#include <stdio.h>
int g; /* 関数の外に書くと、どの関数からも使える */
void f(int a)
{
int b; /* この関数の中でのみ有効 */
a += 2; /* この変更は、main() の中のaには関係ない */
g += 2; /* この変更は、gの値を変更する */
printf("in f() : a = %d\n", a);
printf("in f() : g = %d\n", g);
}
int main()
{
int a=10; /* main() でのみ有効 */
g = 100;
printf("in main() : a = %d\n", a);
printf("in main() : g = %d\n", g);
f(a); /* 変数a(aそのものでなく、aのコピー)をf() に渡す */
printf("in main() : a = %d\n", a);
printf("in main() : g = %d\n", g);
return 0;
}
変数には、その変数の有効な範囲(スコープ)が決められます。例えば、ある関数内で宣言した変数はほかの関数から参照することはできません。そのため、同じ名前の変数を別々の関数内で書くことができます(例えば、iという変数はループカウンタ用によく使います)。
また、関数の外に変数の宣言をおくと、どこの関数からも参照できる変数(グローバル変数)になります。グローバル変数は一見大変便利に見えます。しかし、グローバル変数が本当に必要な場合はそれほどありません。はじめのうちは変数をグローバルにしてしまいがちですが、グローバル変数は極力使わないようにしましょう。変数の値をほかの関数で使いたいときは引数で渡すようにします。
標準ライブラリ関数 |
これまでプログラムを作成する場合必ずファイルの先頭に
#include <stdio.h>
という行を必ず記述してきました。これはプログラムを記述する際に必要となる基本的な関数や設定を記述してあるファイルを読み込むために必要となります。 よって、特に他の設定が必要でないようなプログラムでは記述しなくてもコンパイルすることも実行することもできます。次のプログラムはインクルードファイ ルがなくても動作可能なプログラムです。
/*** インクルードファイルが必要ないプログラム ***/ int main(void){ printf("Hello!\n"); }
ただしこれは極端な例で、通常作成するようなプログラムではインクルードファイルが必ず必要になります。特に"stdio.h"というインクルードファ
イルはCのプログラムの基本となる入出力に関しての関数設定が含まれていますのでどんなプログラムにも必ず記述されています。インクルードファイルはたく
さんの種類があり、利用したい関数によってどのファイルをインクルードすれば良いかが変わります。
例えば文字列の操作を行いたいときに は"string.h"というインクルードファイルに便利な関数が登録されていますし、複雑な計算をしたいときには"math.h"というインクルード
ファイルに数学でよく使う関数が登録されています。さらに、利用する環境(OSなど)に依存するようなインクルードファイルもあります。また自分で作成し
たプログラムをインクルードファイルとして読み込んで利用したりもできますがここでは説明しません。
このようにインクルードファイルを使うことによって既にある便利な関数等の設定が利用できますのでプログラムを作成する前にどんな関数が利用できるかを確認することで余分なプログラムを作成する必要がなくなります。
ここでは、タイピングソフトをつくるのに必要な標準入出力関数、時間関数、文字列操作関数の代表的なものについて説明します。
標準ライブラリ関数に用意されている代表的なヘッダファイルを以下に示します。この他にも多数のヘッダファイルとライブラリ関数が用意されています。
インクルードファイル 説明 1 stdio.h 標準入出力関数のインクルードファイル 2 stdlib.h ユーティリティ関数のインクルードファイル 3 math.h 数学関数のインクルードファイル 4 string.h 文字列操作関数のインクルードファイル 5 time.h 時間関数のインクルードファイル
以下に上記ファイルで定義されている関数の代表的なものについて示します。この他にも多くの関数がインクルードファイル内で定義されています。詳しくは、標準ライブラリ関数を参照してください。
1) 標準入出力関数:stdio.h
戻り値 関数 内容 int printf( const char *format, ・・・ ) 標準出力に書式つきで出力する int sacnf( const char *format, ・・・ ) 標準入出から書式つきで入力する FILE * fopen( const char *filename, const char *mode ) ファイルをオープンする FILE * fclose( FILE *fp ) ファイルをクローズする int fprintf( FILE *fp, const char* format, ・・・ ) ストリームに書式つきで出力する int fscanf( FILE *fp, const char* format, ・・・ ) ストリームから書式つきで入力する int sfprintf( char *s, const char* format, ・・・ ) 文字列に書式つきで出力する int sscanf( char *s, const char* format, ・・・ ) 文字列から書式つきで入力する
2) ユーティリティ関数:stdlib.h
戻り値 関数 内容 int abs( int n ) 整数の絶対値を返す long labs( long n ) 整数の絶対値を返す int atoi( const char *str ) 文字列を整数に変換する int rand( void ) 乱数を返す void srand( unsingned seed ) 乱数列の初期化
3) 数学関数:math.h
戻り値 関数 内容 double sin( double x ) sin(x) の結果を返す double cos( double x ) cos(x) の結果を返す double tan( double x ) tan(x) の結果を返す double asin( double x ) sin-1(x) の結果を返す double acos( double x ) cos-1(x) の結果を返す double atan( double x ) tan-1(x) の結果を返す double fabs( double x ) x の絶対値を返す double pow( double x, doubel y ) xy の結果を返す double sqrt( double x ) √x の結果を返す double log( double x ) loge(x) の結果を返す double log10( double x ) log10(x) の結果を返す
4) 文字列操作関数:string.h
戻り値 関数 内容 char * strcpy( char *str1, const char *str2 ) str1の領域に文字列str2をコピーする char * strcat( char *str1, const char *str2 ) 文字列str1に文字列str2を連結する int strcmp( char *str1, char *str2 ) 文字列str1が文字列str2より小さければ、0より小さい数、大きければ0より大きい数、等しければ0を返す
size_t strlen( const char *str ) 文字列の長さを返す char * strchr( const char *str, int ch ) 文字列中に文字があった場合にその最初の文字位置へのポインタを返す
char * strstr( const char *str1, const char str2 ) 文字列str1の中で文字列str2と一致する最初の位置へポインタを返す
5) 時間関数:time.h
戻り値 関数 内容 time_t time( time_t *tp ) 現在の時刻を返す struct tm* localtime( const time_t *time ) 時刻を日時分・・・の地域時間をあらわす構造体に変換する char * ctime( const time_t *time ) 時刻を文字列に変換する
それでは、標準ライブラリ関数を用いて、タイピングソフトを改良していきましょう。
サンプル4へ
配列変数 |
#include <stdio.h>
#define MAX 5 /* これ以後の MAX を 5 に読み替える命令 */
int main()
{
int i;
int point[MAX]; /* MAX 個の要素を持つ配列 point を宣言 */
int total=0, avr;
for(i=0; i", i+1);
scanf("%d", &point[i]); /* 配列の場合でも & を忘れずに付ける
*/
}
for(i=0; i
これは、何をしているプログラムか意味を読みとれるでしょうか?文を先に読み進める前にまず考えてみてください。
C言語での配列は、ほかの言語でよく使われる小カッコ(())による表現ではなく、大カッコ([])によって表現されます。その点をのぞけば、宣言もプログラム中での参照の方法もイメージ通りに行えることが読みとれると思います。
ここで注意しなければならないのは、配列の添え字(何番目かを示す数字。この場合はiを使っているところ)は0からMAX-1まで使用できる、ということです。point配列変数の、MAX番目(point[MAX]のこと)は存在しません。宣言のところに用いる数は、その配列が「何個の要素を持つか」であって「何番目までの添え字が使えるか」ではありません。このことは、for文のiの値を見てもわかります。iは、0から始まりMAXに達する1つ手前で終わります(for文中の条件式がi<=MAXではないことに注目)。
また、配列の要素数の指定には#defineを使うのが定石です。
このプログラムでは配列の内容をキーボードから入力させていますが、あらかじめ初期化しておくことも可能です。その例を次に示します。
#include <stdio.h>
#define MONTHS 12
int main()
{
int day[MONTHS+1] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}; /* 配列の初期化は中カッコ でくくる */
int input;
printf("今日は何月ですか? >");
scanf("%d", &input);
printf("すると今月は %d日までだから、", day[input]);
if(day[input] == 31) /* 単文だから中カッコを省く */
printf("月末にはダイクマへ行きましょう\n");
else printf("サーティーワンの日はないのですね\n");
return 0;
}
それでは、このテーマのアニメーションでの解説付例題をやってみましょう。
例題3へ
次に、この章で学んだことを使ってタイピングソフトを完成させましょう。
タイピングソフトの完成へ