関数の定義
関数の定義について解説します。関数とは一つの機能をまとめたものです。関数名、引数名とその型と個数、戻り値の型、処理の内容を記述します。
// 関数の定義 戻り値の型 関数名(引数の型1 引数名1, 引数の型2 引数名2, ...) { // 処理の内容 }
定義した関数を呼び出すときは、以下のように書きます。
// 関数呼び出し 関数名(値1, 値2, ...);
関数の呼び出しの前に、「関数の定義」あるいは「関数のプロトタイプ宣言」によって、関数名と引数の型と個数、戻り値の型がわかっている必要があります。
Hello World!と出力する関数
まず最初に、引数も戻り値もない関数を定義して、関数を呼び出してみましょう。引数がない場合は「void」を指定します。戻り値がない場合は「void」を指定します。voidは、存在しないことを表現する特別な型です。もし、関数定義の引数の部分でで「print_hello()」のように、voidを書かなかった場合は「型が不明の任意の個数の引数」を意味するので注意してください。
#include <stdio.h> // 関数の定義 void myapp_print_hello(void) { printf("Hello World!\n"); } int main (void) { // 関数呼び出し myapp_print_hello(); }
出力結果です。
Hello World!
二つのint32_t型の数値を足す関数
二つのint32_t型の数値を足す関数を定義してみます。
#include <stdio.h> #include <stdint.h> // 関数の定義 int32_t myapp_sum(int32_t num1, int32_t num2) { int32_t total = num1 + num2; return total; } int main (void) { // 関数呼び出し int32_t num1 = 1; int32_t num2 = 5; int32_t total = myapp_sum(num1, num2); printf("%d\n", total); }
出力結果
6
動的に生成した配列の和を求める関数
calloc関数で動的に生成した配列の和を求める関数です。ポインタ型を引数にするのがポイントです。
#include <stdio.h> #include <stdint.h> #include <stdlib.h> // 関数の定義 int32_t myapp_sum_array(int32_t* nums, int32_t nums_length) { int32_t total = 0; for (int32_t i = 0; i < nums_length; i++) { total += nums[i]; } return total; } int main (void) { // 関数呼び出し int32_t nums_length = 3; int32_t* nums = calloc(sizeof(int32_t), nums_length); nums[0] = 1; nums[1] = 2; nums[2] = 3; int32_t total = myapp_sum_array(nums, nums_length); printf("%d\n", total); free(nums); }
出力結果。
6
構造体の内容を引数に渡す
動的に生成した構造体の内容を変更する関数を書いてみます。ポインタ型で引数を定義するのがポイントです。
#include <stdio.h> #include <stdint.h> #include <stdlib.h> // 構造体の定義 struct myapp_book { int32_t id; const char* name; }; // 関数の定義 void myapp_init_book(struct myapp_book* book) { book->id = 1; book->name = "C99 Tutorial"; } int main (void) { struct myapp_book* book = calloc(sizeof(struct myapp_book), 1); // 関数呼び出し myapp_init_book(book); printf("id:%d, name:%s\n", book->id, book->name); free(book); }
出力結果。
id:1, name:C99 Tutorial
関数のプロトタイプ宣言
上記で関数定義について解説してきましたが、実務では、より推奨された書き方があります。
それは、関数のプロトタイプ宣言をヘッダで行い、関数定義をソースコードで行う分割コンパイルの手法です。
関数のプロトタイプ宣言は以下のように書きます。引数名は省略できます。戻り値の型、関数名、引数の型は、関数の定義で行ったものと同じにします。
// 関数のプロトタイプ宣言 戻り値の型 関数名(引数の型1 引数名1, 引数の型2 引数名2, ...);
C言語では、関数定義は関数呼び出しより前にある必要があります。
関数定義が関数呼び出しより後ろにある場合は、コンパイルエラーになると想定しておくのがよいです。
gccは暗黙的に解決を行い、警告を出します。
#include <stdio.h> #include <stdint.h> int main (void) { // 関数呼び出し int32_t num1 = 1; int32_t num2 = 5; int32_t total = myapp_sum(num1, num2); printf("%d\n", total); } // 関数の定義が関数呼び出しより後ろにある int32_t myapp_sum(int32_t num1, int32_t num2) { int32_t total = num1 + num2; return total; }
gccでは警告が発生。
a.c:8: warning: implicit declaration of function ‘myapp_sum’
関数のプロトタイプ宣言を関数呼び出しの前に追加してみましょう。警告がでなくなります。
#include <stdio.h> #include <stdint.h> // 関数のプロトタイプ宣言 int32_t myapp_sum(int32_t num1, int32_t num2); int main (void) { // 関数呼び出し int32_t num1 = 1; int32_t num2 = 5; int32_t total = myapp_sum(num1, num2); printf("%d\n", total); } // 関数の定義 int32_t myapp_sum(int32_t num1, int32_t num2) { int32_t total = num1 + num2; return total; }
関数プロトタイプ宣言では、引数名を省略して、型と関数名だけにしておくのが、引数名のマクロ展開を回避できるため、より良いとされているようです。
#include <stdio.h> #include <stdint.h> // 関数のプロトタイプ宣言(より良いが、そのままコピペできぬ...グっ。) int32_t myapp_sum(int32_t, int32_t); int main (void) { // 関数呼び出し int32_t num1 = 1; int32_t num2 = 5; int32_t total = myapp_sum(num1, num2); printf("%d\n", total); } // 関数の定義 int32_t myapp_sum(int32_t num1, int32_t num2) { int32_t total = num1 + num2; return total; }
分割コンパイルの例
分割コンパイルの例を書いておきます。実務でC言語を書く場合は、この方法で記述することをお勧めします。
myapplib.h
ヘッダファイルです。関数のプロトタイプ宣言を書きます。「#ifndef MYAPPLIB_H」の部分はインクルードガードです。
#ifndef MYAPPLIB_H #define MYAPPLIB_H #include <stdint.h> // 関数のプロトタイプ宣言 int32_t myapp_sum(int32_t num1, int32_t num2); #endif
myapplib.c
関数定義をしたC言語ソースファイルです。
#include <stdint.h> // 関数の定義 int32_t myapp_sum(int32_t num1, int32_t num2) { int32_t total = num1 + num2; return total; }
myapp.c
アプリケーションの本体のC言語ソースコードです。「myapplib.h」を読み込んでいます。
#include <stdio.h> #include <stdint.h> #include "myapplib.h" int main (void) { // 関数呼び出し int32_t num1 = 1; int32_t num2 = 5; int32_t total = myapp_sum(num1, num2); printf("%d\n", total); }
コンパイルしてリンクして実行
gcc -c myapplib.c gcc -c myapp.c gcc -o myapp myapp.o myapplib.o ./myapp
出力結果。
6