C言語の勉強中に、「プロトタイプ宣言って必要なの?」と思ったのでメモ。
#プロトタイプ宣言とは
プログラミングでは、原則としてソースは上から処理されます。これはC言語でもPythonでもbashスクリプトでも同じです。
なので普通は関数は使う前に定義しておく必要があります。例えば「Hello world!」と表示する関数を使おうと思えば、次のようになります。
#include<stdio.h>
void hw(void){
puts("Hello world!");
}
int main(void){
hw();
}
※実はこのコードはあまりよろしくないようです。コメントありがとうございます。(2018.05.31)
→修正しました(2019.09.16)
ところが、mainを最初に書いて後で関数の処理を定義したいこともあります。main関数を上に書いてその中に出てくる関数を下につらつらと書いた方が構造が分かりやすくなります。長いソースコードでmainを最後に書いていると、デバッグの際にいちいちスクロールしなければなりません。
それでも定義されていない関数は使えません。そこで使う関数の名前だけを先に定義しておく機能を使います。これがプロトタイプ宣言です。
#include<stdio.h>
void hw();
int main(){
hw();
}
void hw(){
puts("Hello world!");
}
#GCCではプロトタイプ宣言無しでも通ってしまう
ではプロトタイプ宣言を書かないとどうなるのでしょうか。
#include<stdio.h>
int main(){
hw();
}
void hw(){
puts("Hello world!");
}
clangでは、当然エラーが出てコンパイルできません。
https://wandbox.org/permlink/ZmeMBpjFL8OL3hPv
prog.c:5:5: warning: implicit declaration of function 'hw' is invalid in C99 [-Wimplicit-function-declaration]
hw();
^
prog.c:8:6: error: conflicting types for 'hw'
void hw(){
^
prog.c:5:5: note: previous implicit declaration is here
hw();
^
1 warning and 1 error generated.
ところが、gccでは警告は出るもののコンパイルは通ってしまいます。
test.c: In function ‘main’:
test.c:8:3: warning: implicit declaration of function ‘hw’ [-Wimplicit-function-declaration]
hw();
^
test.c: At top level:
test.c:11:6: warning: conflicting types for ‘hw’
void hw(){
^
test.c:8:3: note: previous implicit declaration of ‘hw’ was here
hw();
とはいえ警告が出ることはよろしいことではないですし、処理系によってコンパイルが通らなくなるのは困るので、プロトタイプ宣言はきちんとしておいたほうがよさそうです。実際にはヘッダファイルに書いてincludeするのが多いみたいです。
#そもそもプロトタイプ宣言がいらない言語はあるのか
ここからは個人的な話ですが、僕はmainを先に書いて関数はあとで書く方が好きです。なんでそこで「プロトタイプ宣言しないといけないなんて、C言語は面倒だな」と思ったわけです。
なので「プロトタイプ宣言しなくても関数を後で定義される言語って何かあったっけ」となったわけです。ですが、冷静に考えてみれば、多くの言語ではソースは上から処理されるので、関数を後に書くことはできません。
たとえばPythonでは次のようになります。
def hw():
print("Hello world")
hw()
bashスクリプトでも同じです。少なくともスクリプト言語では関数はちゃんと順番通り定義しないといけません。では一体何の影響でmainを先に書くようになったんでしょう。
そこでやっと思い出しました。やつです。fortranです。
program hello_world
implicit none
call hw()
contains
subroutine hw()
write(6,*) "Hello world!"
end subroutine hw
end program hello_world
モダンなfortranでは、メインとなるブロック以外の関数はすべてcontains文の下に書くのが普通で、むしろメインより前に書くことができません。fortran、恐るべし...。