C言語講座/関数について

【関数について】

このページを見るときは固定等幅フォントを使用してください


関数とは

まずC言語は関数ありきです.ではその関数とは何でしょうか?
まず中学1年の頃の数学の授業を思い出してください(私はあまり良い想い出は無いのですが).関数とは基本的にブラックボックスと教わった人が多いかと思います.
例えば y = ax + b という式は x に数値を入れていくことである答えが出てきます.この時 y と x の間にはある規則,関係が成り立っています. この時 y = f(x) 等で表し,これを関数と呼びます. よって関数とは実行することである規則をもつ答えつまりある仕事をするという解釈もできます.

C言語は関数で成り立っています.つまりある関数を実行することである仕事をするプログラムとも言えます.関数一つ一つは大した事をしませんが,それらの関数をあるアルゴリズムで並べることにより複雑な仕事をします.
例えば printf() という関数は見た目には文字を画面に表示するという仕事しかしませんが,この関数を使用することにより画面に表示するプログラムを自分で書く必要が無い(画面に文字やグラフを書くのは結構面倒なんですよ)ばかりか,プログラム的にも非常にすっきり見えることになります.

さて一番基本中の基本のC言語でのプログラムが「Hello!」プログラムです.このプログラムはK&Rの著書でも一番最初に出てくるプログラムです.


Program 1-1
void main( void )
{
    printf("Hello!") ;
}

もし printf() という関数がなければ前述したように自分で画面表示のプログラムを書かなければなりません.もしアセンブラで
"Hello World"を表示させようとするだけでも10行以上のプログラムが必要となります.
このようにC言語のライブラリには,printf()を含むすでに定義してある基本的な仕事をする関数が種々あり,また自分で定義した関数(作った関数)も同様に簡単に使うことが出来ます.
例えば y= 2x + 10 を計算する関数を y() として定義します.この関数にある数を入れて答えを出すには下記のようなプログラムが例として考えられます.
Program 1-2
void main( void )
{
    int i , kotae ;
    for ( i=1 ; i==100 ; i++ ) {
        kotae = y(i) ;
        printf("%d \n" , kotae ) ;
    }
}

int y ( int x )
{
    int ans ;
    ans = x*2 + 10 ;
    return ( ans ) ;
}

上記のプログラムは式 y= 2x + 10 の x に 1 〜 100 までの数値を代入し,その答えを表示するプログラムとなっています.あえてここでは簡単にも関わらず式の内容を関数として定義してしていますが,関数化する事により大元の main 関数は何となくすっきりとした形に見えます.
y() という関数の中身はある仕事をする関数として取り扱うことにより,プログラムをしていく上で全体を見渡せるようなプログラムが出来ますし,関数化する事によりここの仕事毎にプログラムを作ることが出来ますので,作業効率,問題(誤り)の修正のしやすさ(業界用語ではデバックといいます),後からの仕様変更のし易さ等々関数化するメリットは非常に大きいと言えます.
このようにC言語での関数とは非常に大事な 概念 ですので,その意味をしっかりと理解してください.


関数の定義

関数は以下の様な構造になっています.

[記憶クラス][型指定]関数名([引数])

{
関数の中身
}

記憶クラスには static(静的変数)と extern(外部変数)のどちらかとなります(後述). 省略をするとexternとして処理をされます.

型指定は配列型と関数型(他で宣言した関数?)以外は使えます(後述).省略をするとint型として処理をされます.

関数名は自分でわかりやすい関数名を付けてください.但し既にC言語のライブラリで登録されている名前は付けられません.出来ればアルファベットとアンダーバー _,ハイフン - 等で関数名は付けたほうが無難です.

引数は定義した関数にどんな値をどれだけ渡すのかを表記します.別段渡すものがなければ書く必要はありませんが,何も引数が無いことを明示しておくために void と表記しておくのがいいでしょう

関数の中身については {} の間で囲まれた部分に記載します.
まぁ細かいことは徐々に勉強をしていきましょう.


main 関数

さてC言語で一番大切な関数は main関数 です
実行するプログラムの中には必ず main関数 が入っていなければならず,プログラムはこの main関数 の最初から実行されます.
Program 1-1Program 1-2の場合も最初は main関数 の頭から実行されます. Program 1-1の場合は printf()文しかありませんのでわかるとおもいますが,Program 1-2の場合は最初に変数の宣言を行い( int i , kotae ; ) ,次に実際のプログラムが書かれています.また下には y() という関数があり,これは main関数から呼び出されます.
どんなに複雑なプログラムでも一番最初に実行するのは main関数 ですので, main関数 さえ探せばなんとか人のプログラムもわかるとっかかりが出来るというものです.


プロトタイプ宣言

さて実際に自分で色んな関数を作ったり,C言語のライブラリから色々と関数を使うようになった場合,その関数の引数や型がどんなタイプのものであるかを確認することが出来ます.
実際にコンパイル時に各関数の型や引数等が間違っていた場合エラーが表示されますし,また実際の関数が実行される際にも正しい型でデータの渡したり出来ますので,プログラム全体の間違いは減るようになります.
実際どの様にプロトタイプ宣言は記述するのでしょうか.下記のプログラムで見てみましょう.これは Program 1-2 を手直ししたものですが,※の部分がプロトタイプ宣言の為の追加部分となっています.
Program 1-3
    #include    "stdio.h"

    int y( int ) ;          ← ※

void main( void )
{
    int i , kotae ;
    for ( i=1 ; i==100 ; i++ ) {
        kotae = y(i) ;
        printf("%d \n" , kotae ) ;
    }
}

int y ( int x )
{
    int ans ;
    ans = x*2 + 10 ;
    return ( ans ) ;
}

※部分,つまり int y( int ) ; がプロトタイプ宣言となります.
この意味は 関数 y() の関数型は int型 であり,引数は int型 の引数が存在する事を示しています.
コンパイラの際にはこのプロトタイプ宣言が main 関数内の y() と実際の関数 y() の各種型や引数などを確認しています.間違っていれば警告かエラーがコンパイル時に出されます.


関数間のデータの受け渡し

関数と関数におけるデータの受け渡しには3つの方法があります.
Call by Value
この方法は非常に一般的な方法です.比喩的に言うとAと言う人からコップに入ったジュースをBと言う人のコップに渡すのに似ています.この時コップは何でもよく,ジュースという中身が非常に大事になります.
つまり一度に2つのデータを渡そうとするとき,お互い2つの容器が必要になります.
         A                         B
    +---------+               +---------+
    |  [ 1 ]--|---------------|->[ 1 ]  |
    +---------+               +---------+
    +---------+               +---------+
    |  [100]--|---------------|->[100]  |
    +---------+               +---------+
実際にプログラムの関数で見てみます.Program 1-3 を見てみましょう.この時 main 関数は y() という関数に変数 iを通じて 1〜100までの値を渡しています.
y() ではもらった値を int x にて受け取り,内部で計算しています.
この方法は実際の値の受け取りをしていますので,受け取る変数(コップ)はそれぞれの関数で違う容器を用意していることになります.

Call by Refrence
この方法は一寸理解するのに時間がかかるかもしれません.比喩的に言うとあるコップがあり,Aと言う人がジュースを入れてそのコップの在処をBに教えるのに似ています. Bは教えてもらったコップ在処から中身を取りだして利用しますが,コップの在処にはいくつものコップを存在させることが出来ます.

         A                         B

         |                         |
         |       +---------+       |
         +------>| Addres  |<------+
                 +---------+
                      A
                      |    +---------+
                      +--->|  [ 1 ]  |
                      .    +---------+
                      .    +---------+
                      +--->|  [100]  |
                           +---------+
この方法はポインタと言われる方法で,少々概念的に難しいかもしれません.
まずコップはデータが保存出来るメモリであり,またそのコップの在処を示すと言うことはそのデータが保存出来るメモリの住所(Address)を指している事になります.
つまりこの方式はデータが保存してある住所(Address)を渡すことでAもBもデータの共有が出来ることになります.
Program 1-4
    #include    "stdio.h"

    int y( int * ) ;                    ← ※(1)

void main( void )
{
    int i , kotae ;
    for ( i=1 ; i==100 ; i++ ) {
        kotae = y( &i ) ;               ← ※(2)
        printf("%d \n" , kotae ) ;
    }
}

int y ( int *x )                        ← ※(3)
{
    int ans ;
    ans = (*x) * 2 + 10 ;               ← ※(4)
    return ( ans ) ;
}

Program 1-4 はこのアドレスを渡す方式で書かれています.特にProgram 1-3 と比べて※の部分が変更されています.
※(1)はプロトタイプ宣言で関数 y() の引数がアドレスによるデータのやりとりをする事を宣言しています.
※(2)は変数 i の前に & を付けることで,変数の在処(Address)を渡すようにしています.これは後述します.
※(3)は関数 y() の引数がアドレスによるデータのやりとりをする事を意味しており,関数 y() におけるやりとりのため変数の名前を x としています.
※(4)は関数 y() の内部にて,main 関数からのデータを取りだし( *x がそれにあたります)計算をしています.これらの一連の操作はポインタと呼ばれる概念でおこなわれています.

外部変数渡し
工事中


前に戻る
最初へ戻る