C初級:関数の作り方と関数の種類

C言語初級カテゴリのロゴ画像 C言語初級

結論から言うと、C言語はmain関数やprintf関数などの関数を組み合わせてプログラムを記述します。
関数は自分で作ることも可能です。

関数とは?

C言語は、関数という命令の集まりを組み合わせることでコンピュータへの指示を実現しています。
一番よく使う関数は、main関数です。

#include <stdio.h>

int main(void)
{
	printf("Hello!\n");

	return 0;
}

C言語は、main関数からプログラム実行を開始します。
上記のプログラムでは、main関数の中からプログラム処理が開始され最初のprintfが実行されます。
ディスプレイに「Hello!」が出力されて処理を終わります。
ちなみにprintfも関数です。C言語では、関数の中で別の関数を呼び出すことが出来ます。

return文の意味

ところでreturn 0;は何をしているのかと言うとこのmain関数の呼び出し元に0を返すという役割をしています。

イメージ
関数の書き方

ここでいう呼び出し元とは、つまり呼び出している環境にもよりますが、あなたが実行した環境に戻すということになります。
わたしはWindowsのコマンドプロンプト上でC言語プログラムをコンパイルして実行したので、わたしの場合、コマンドプロンプトに処理が返るということになります。
このとき値として0をコマンドプロンプトに返しているのですが、コマンドプロンプトの場合は、どんな値が返ってきてもうんともすんともしません。ですからreturn 0;return 100;return -100;に変えたところで実行結果は変わりません。

補足するとC言語は制御系プログラムと言われていて、例えばクロネコヤマトのお兄ちゃんが持っている端末がC言語で作ったプログラムが動作している環境だとすると、そうした環境では、main関数でreturn 0;では正常終了で緑ランプが点灯したり、return -1;では赤いランプが点灯するように作られているかもしれないのです。そうした実際の機器でないとmain関数のreturn文の意味はうすいかもしれません。

関数の作り方

このmain関数は、もともと存在している関数です。main関数の中で実行したprintfも関数です。
このようにC言語プログラムは、関数の組み合わせで出来ていると言えます。
そしてここが一番大事な部分ですが、関数は自分で作ることもできます。

あなたが山田さんならyamadaという名前の関数を作ることも可能ということです。
試しに作ってみます。

yamada.c

/* yamada.c : 関数yamadaを作る */
#include <stdio.h>
 
void yamada(void)    /* yamadaという名前の関数を定義 */
{
    printf("山田っす!\n");
}
 
int main(void)
{
    yamada();    /* yamada関数を呼び出す */
     
    return 0;
}

実行結果

山田っす!

解説
int main(void)の手前にある部分が関数yamadaを定義している部分です。
例えば整数型のxという変数を定義するとき

int x;

と書きます。これと同じ感覚で関数yamadaを定義しているといった感じでしょうか。
以下が関数yamadaを定義している部分です。

void yamada(void)    /* yamadaという名前の関数を定義 */
{
    printf("山田っす!\n");
}

yamadaの先頭にvoidとあるのは、このyamada関数が戻り値を持たない型だからです。(戻り値を持たないという意味は後述します)

関数の書き方です。

void 関数名(引数){
/* その関数の処理を記述 */
}

関数名は、呼び出すときに使います。
引数については、今回voidとなっています。(引数は次の項で説明します)
引数にvoidを指定すると引数なしという意味になります。

関数は宣言しただけでは実行されません。実行するには、その関数名を指定して呼び出す必要があります。
以下の部分です。

int main(void)
{
    yamada();    /* yamada関数を呼び出す */
     
    return 0;
}

関数は引数を指定するための()を伴って呼び出します。
今回は引数部分がvoidで引数なし関数のため、yamada();として呼び出しています。

関数の引数について

関数は、呼び出す時に関数の処理に対していくつかの数値や文字、文字列などを与えて実行ができます。
関数に引数を渡すという言い方をすることもあります。

以下の例では、関数jijoに整数値を渡して実行しています。

/* hikisu.c : 引数ありの関数 */
#include <stdio.h>
 
void jijo(int x)    /* jijoという名前の関数を定義 */
{
    printf("%dを2乗すると%dです。\n", x, x*x);
}
 
int main(void)
{
    jijo(5);    /* jijo関数を引数に5を指定して呼び出す */
     
    return 0;
}

実行結果

5を2乗すると25です。

解説
引数は関数名の後の()内に記述します。
関数に引数を宣言する際は、引数の変数型宣言が必要になります。

例えば

jijo(5);

と呼び出して実行したい場合の宣言は、

void jijo(int x)
{
/* 処理内容 */
}

となります。
この場合のint xの部分はint yとしてもint aとしても意味は変わりません。
引数に指定した変数名はこの関数内部だけで有効な変数という扱いです。
関数を実行したとき、引数に宣言した変数名を通じて数値や文字データを扱うことができるよといった意味です。

関数jijo全体を見てみます。

void jijo(int x)    /* jijoという名前の関数を定義 */
{
    printf("%dを2乗すると%dです。\n", x, x*x);
}

関数jijoで引数宣言したint xは、printf関数で使われています。
考え方としては、jijo(5)で関数jijoを呼び出したときx = 5が実行されている形になります。

例えばmain関数内で、

jijo(5);
jijo(10);

と2回続けてjijo関数を呼び出した場合は、1回目の実行では、xに5が代入され、2回目の実行では、xに10が代入されて実行されるということです。

ちなみにこれはコンパイルエラーです。

#include <stdio.h>
 
void jijo(int x)
{
    printf("%dを2乗すると%dです。\n", x, x*x);
}
 
int main(void)
{
    jijo(2.5);
     
    return 0;
}

3行目の関数宣言の引数の型と10行目の呼び出しの引数の型が違っているからです。

引数には型宣言が必要、呼び出し元と呼び出し先(関数宣言部分)で型が一致している必要がある。

関数の種類

関数には大きく分けて2つの種類があります。
戻り値を持たないvoid型戻り値を持つ型(char, int などの変数型やstruct型などC言語で扱うことが出来るデータ単位全て)です。

関数の最後にreturn文が必要か必要でないかで分けることもできます。

戻り値を持つ型(void型)return文必要ない
戻り値を持つ型return文必要

戻り値を持たない型(void型)

実行して終わりの関数です。一番シンプルな関数と言えます。
冒頭で登場した関数yamadaはvoid型(戻り値を持たない型)です。
関数の最後にreturnは必要ありません。

void yamada(void)    /* yamadaという名前の関数を定義 */
{
    printf("山田っす!\n");
}

ただし、以下のようにreturn文を記述することはできます。これも戻り値がないという意味です。

void yamada(void)    /* yamadaという名前の関数を定義 */
{
    printf("山田っす!\n");
    return;
}

戻り値を持たない型(引数なし)のサンプル

AからZまで表示する関数print_a2z

/* 31function.c 関数を作る(void型) */
#include <stdio.h>
 
void print_a2z(void){
    int i;
    for(i='A'; i<='Z'; i++) printf("%c", i);
    printf("\n");
}
 
int main(void)
{
    printf("関数を呼び出します\n");
     
    print_a2z();    /* 関数を呼び出す */
     
    printf("関数を呼び出しました\n");
     
    return 0;
}

実行結果

関数を呼び出します
ABCDEFGHIJKLMNOPQRSTUVWXYZ
関数を呼び出しました

戻り値を持たない型(引数あり)のサンプル

指定した範囲のASCIIコード文字を出力する関数print_x2y
引数xからyまでの番号の範囲の文字を出力している。

ASCIIコードについては以下の記事を参照

/* 32function.c 関数を作る(void型:引数有り) */
#include <stdio.h>
 
void print_x2y(int x, int y){
    int i;
    for(i=x; i<=y; i++) printf("%c", i);
    printf("\n");
}
 
int main(void)
{
    printf("関数を呼び出します\n");
     
    print_x2y(48, 57);        /* 関数を呼び出す */
    print_x2y(65, 90);        /* 関数を呼び出す */
    print_x2y(97, 122);    /* 関数を呼び出す */
     
    printf("関数を呼び出しました\n");
     
    return 0;
}

実行結果

関数を呼び出します
0123456789
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
関数を呼び出しました

戻り値を持つ型(int, double, char…型)

関数の呼び出し元に値(数値や文字列などのデータ)を返す関数です。

単純な例で示します。

modoriti.c

/* modoriti.c: 戻り値 */
#include <stdio.h>
 
int jijo(int x){    /* int型関数jijoの宣言部分 */
    return x * x;
}
 
int main(void)
{
    int x;
    x = jijo(5);    /* 関数jijoの呼び出し部分 */
    printf("%d\n", x);
     
    return 0;
}

実行結果

25

解説
関数jijoの呼び出し元は、main関数内の

x = jijo(5);

jijo(5)部分です。

関数の宣言部分の冒頭は、戻り値の変数型と合わせる必要があります。
int jijo(int x){}と関数hogeを宣言している場合、関数jijoの戻り値はint型の数値範囲である必要があります。

戻り値はreturn文の後に定数または変数を使って指定します。上記例では、return x * xとして、呼び出し元に引数xの2乗の値を返しています。

呼び出し元に2乗を返すのイメージですが、
jijo(5)が実行されreturn x * x;が実行されると呼び出し元のjijo(5)の部分が

x = 25;

に置き換わると考えて下さい。これが呼び出し元に返すイメージです。
上記main関数では、この説明の為に変数xを宣言しましたが、以下のようにしても結果は同じです。

modoriti2.c

/* modoriti.c: 戻り値 */
#include <stdio.h>
 
int jijo(int x){
    return x * x;
}
 
int main(void)
{
    printf("%d\n", jijo(5) );
     
    return 0;
}

戻り値は、呼び出し元に返るためこの場合、printfの中にあるjijo(5)の部分が戻り値25に置き変わります。

イメージ
printf(“%d\n”, 25);

戻り値を持つ型(int型関数)のサンプル

厄年をもとめる関数:戻り値 int型整数

/* 33function.c 関数を作る(int型) */
#include <stdio.h>
 
/* 厄年をもとめる関数    数え年で    女33、男42歳 */
int get_yakudosi(int year, int sex){
    int yakudosi;
    /* 男の場合 */
    yakudosi = year + 41;
     
    if(sex == 2)    /* 女の場合 */
        yakudosi = year + 32;
     
    return yakudosi;
}
 
int main(void)
{
    int year, sex, yakudosi;
     
    printf("厄年を教えます\n\n");
    printf("男ですか?女ですか?[男ー>1 女ー>2] ");
    scanf("%d", &sex);
     
    printf("生まれた年を西暦で入力してください: ");
    scanf("%d", &year);
     
    yakudosi = get_yakudosi(year, sex);
     
    printf("\n\nあなたの厄年は、%d年です\n", yakudosi);
     
    return 0;
}

実行結果

厄年を教えます

男ですか?女ですか?[男ー>1 女ー>2] 1 [Enter]
生まれた年を西暦で入力してください: 1970 [Enter]

あなたの厄年は、2011年です

戻り値を持つ型(double型関数)のサンプル

平均体重を求める関数:戻り値double型実数

/* 34function.c 関数を作る(double型) */
#include <stdio.h>
 
/* 平均体重をもとめる関数 */
double get_normal_weight(double height){
    height = height / 100.0;    /* mに直す */
    return (height*height*22.0);
}
 
int main(void)
{
    double height;
     
    printf("平均体重をもとめます\n\n");
    printf("身長を入力してください(cm)");
    scanf("%lf", &height);
         
    printf("\n\nあなたの平均体重は、%.1fkgです\n", get_normal_weight(height) );
     
    return 0;
}

実行結果

平均体重をもとめます

身長を入力してください(cm)171 [Enter]

あなたの平均体重は、64.3kgです

戻り値を持つ型(char型関数)のサンプル

/* 35function.c 関数を作る(char型) */
#include <stdio.h>
 
/* 先頭文字を大文字に変換して返す関数 */
char get_head(char* str){
    if(str[0] >= 'a' && str[0] <= 'z'){    /* 先頭文字が英小文字の場合 */
        str[0] = str[0] - 32;
    }
    return (str[0]);
}
 
int main(void)
{
    char mojiretu[256];
    char initial;
     
    printf("あなたの名前をローマ字で入力して下さい: ");
    scanf("%s", mojiretu);
     
    initial = get_head(mojiretu);
    printf("イニシャル%c\n", initial );
     
    return 0;
}

実行結果

あなたの名前をローマ字で入力して下さい: Yamada [Enter]
イニシャルY

解説
文字列はchar型の配列ですので全体を引数として渡すことはC言語ではできません。
その場合は、ポインタを使います。char型の配列であればchar *という型になります。配列の先頭アドレスを渡すという意味になります。詳しくはポインタの項(未記述)で解説します。

戻り値を持つ型(char*型関数)のサンプル

文字列(char型の配列)の先頭文字が小文字の場合大文字に変換して返す関数

/* 36function.c 関数を作る(char型配列のポインタ) */
#include <stdio.h>
 
/* 文字列の先頭を英大文字にして返す関数 */
char* get_initial(char* str){
    if(str[0] >= 'a' && str[0] <= 'z'){    /* 先頭文字が英小文字の場合 */
        str[0] = str[0] - 32;
    }
    return (str);
}
 
int main(void)
{
    char mojiretu[256];
    char *p;
     
    printf("あなたの名前をローマ字で入力して下さい: ");
    scanf("%s", mojiretu);
     
    p = get_initial(mojiretu);
    printf("[%s]\n", p );
     
    return 0;
}

実行結果

あなたの名前をローマ字で入力して下さい: Yamada [Enter]
[Yamada]

コメント

タイトルとURLをコピーしました