C言語のおさらい プログラムのモジュール化の考え方・使い方

モジュール化とは

プログラムが複雑化し大きくなればなるほど、わかりにくくなります。
それをわかりやすくするために、プログラムをいくつかの小さな部分(モジュール化)に分割していく必要がある。
これを「プログラムのモジュール化」といい、構造化プログラムのもう一つの考え方である。

「プログラムのモジュール化」のメリットがいくつかある。
  • 一つ一つのモジュールは小さいのでわかりやすい。
  • モジュール単位で機能がまとまっていると、プログラムの変更(改良)がしやすい。
  • 大きなプログラムを複数の人間で分担して開発できる。
一つのモジュールはなるべく一つの機能だけを持つようにする。これがモジュール化の重要なところ。



関数とは

C言語では分割したモジュールを「関数」として扱う。なので、「C言語プログラムとは、関数の集まりである」といえる。

今まで使ったprintf()関数で考えてみる。データを一定の書式に従ってディスプレイに表示するには、さまざまな命令を合わせる必要がある。この命令群を毎回書いていてはプログラムが大きくなって、わかりにくくなる。
そこで、命令群をモジュール(関数)として分割すれば、プログラムを作成するときに出力の命令群を毎回書く必要がなくなり、負担が少なくなる。

C言語では、よく使われる関数は、あらかじめヘッダファイルに用意してある。このヘッダファイルをプログラムの1行目に「#include <stdio.h>」と書くことで、好きなように使える。このような関数を「標準関数」という。

標準関数以外の命令をモジュール化したいときは、プログラム作成者自身が関数として定義することができる。定義の仕方は、分割したモジュールに名前(関数名)をつけて、別の場所に書くだけ。あとは使いたいときにモジュールの関数名を指定すればいい。これを「関数の呼び出し」という。



関数のデータのやり取りの仕方

引数と戻り値

C言語の関数でデータのやり取りをするるには、「引数」と「戻り値」を使う方法が簡単である。引数とは、関数に入力するデータで、戻り値とは関数から出力されるデータである。引数は必要に応じて複数指定できるが、戻り値は一つしかない。
呼び出した側から渡す引数を「実引数」、受け取る側の引数を「仮引数」という。


関数の型宣言

関数を使う場合に決めなければならないことが二つある。
  • 「引数」はどんなデータ型を受け取るのか。
  • 「戻り値」はどんなデータ型を返すのか。
この二つを定義するのが「関数の型宣言」である。
関数型 関数名(引数型1 仮引数1, 引数型2 仮引数2 ・・・){
 
 モジュール化したい命令群;

 return (式);
}

  • 関数型には戻り値の型を指定する。(基本的にはデータ型とおなじもの)
  • 戻り値がない場合は、関数型として「void」を書く。
  • 引数型には引数の型を指定する。(基本的にはデータ型とおなじもの)
  • 引数がない場合は、「void」を書くか省略する。
  • return 文を実行すると関数は終了する。
  • return()内の式には、戻り値を書く。()は省略してもよい。(省略されることが多い)
  • 戻り値がない場合は次の方法で関数を終了する。
    1. return文を省略する。その場合、最後の「}」まで実行して関数は終了する。
    2. 「return ;」のみ書く。この場合()を書いてはいけない。

関数の呼び出し書式

関数名(実引数1, 実引数2 ・・・);

  • 引数には、変数・定数・式などを書く。
  • 引数がない場合でも()は省略できない。

仮引数名と実引数名は、同じでも違っていてもよい。引数は名前とは関係なく、書いた順番に渡していく。



プログラムを分割してみる

簡単なプログラムで分割したものを書いてみる。

#include <stdio.h> 
int kasan(int x, int y) {
 int z;

 z = x + y;

 return z;
}

int main(void) {

 //変数の宣言
 int a;
 int b;
 int c;

 //入力
 a = 6;
 b = 9;

 //処理
 c = kasan(a, b);

 //出力
 printf("答えは%d\n", c);

 return 0;
}

実行結果がこんな感じになる。

プログラムを分割するときに注意するところがある。
プログラムは上から実行されるので、分割したプログラムは上に書く必要がある。
基本的に上で書いた関数が、下の関数で使えるようになる。
「#include <stdio.h>」が1行目に書かれている理由がこのためである。
1行目にこれが書かれているから、下の関数で「printf()関数」が使えるのである。


プロトタイプ宣言

C言語のプログラムでは、多数の関数を使います。短いプログラムであれば問題ないが、長いプログラムになると、各関数が「どんな引数を使うのか」「どんな戻り値がかえってくるのか」がわかりにくくなってくる。加えて、サブの関数が増えることで、メインとなる関数が下のほうに行ってしまい、何のプログラムかわからなくなってしまう。
それを防ぐために、関数の「プロトタイプ宣言」を行う。

プロトタイプ宣言の書式

関数型 関数名(引数型1 引数1, 引数型2 引数2 ・・・);

基本的に関数のプロトタイプ宣言は、main() 関数の前で行う。
main() 関数内で行うと、main() 関数内でしか使えなくなる。


プロトタイプ宣言を使って関数を書いてみる

プロトタイプ宣言を使って書くと、このような感じになる。

#include <stdio.h> 

int kasan(int x, int y);

int main(void) {

 //変数の宣言
 int a;
 int b;
 int c;

 //入力
 a = 6;
 b = 9;

 //処理
 c = kasan(a, b);

 //出力
 printf("答えは%d\n", c);

 return 0;
}

int kasan(int x, int y) {
 int z;

 z = x + y;

 return z;
}

自作したkasan関数がmain() 関数の下に書かれてもビルドエラーにならなくなる。
自作した関数を多用するのであれば、プロトタイプ宣言は必須である。


グローバル変数とローカル変数

変数の宣言は、関数内で行うものと関数の外側で行うものの2種類がある。
関数内で行われるものはローカル変数と呼ばれる。ローカル変数は宣言を行った関数内でしか使えない。
関数の外側で宣言された変数をグローバル変数という。グローバル変数は複数の関数で使うことができる。

どういったものか実際に書いてみる。

#include <stdio.h> 

int kasan(int x, int y);

int z;

int main(void) {

 //変数の宣言
 int x;
 int y;

 //入力
 x = 6;
 y = 9;
 z = 3;

 //処理
 printf("今、zは%d\n", z);
 z = kasan(x, y);

 //出力
 printf("答えは%d\n", z);

 return 0;
}

int kasan(int x, int y) {

 z = x + y;

 return z;
}

実行結果がこうなる。

関数の外側で宣言されたものは、複数の関数で使っても問題ないことがわかる。
また、ローカル変数はグローバル変数と同じ名前の場合にはローカル変数が優先される。
グローバル変数とローカル変数を使うときには注意する。






コメント

このブログの人気の投稿

STM32CubeIDEに日本語化プラグインを導入する

STM32F401でLチカをする(LED点灯)

STM32F401-Nucleoでシリアル通信をやってみる