C++:函數先聲明后實現


  賊神奇的是,直到昨天在寫flex規則的時候我才知道C++中的函數要么在使用之前先定義,要么將實現放在調用之前,不允許先調用后實現。之前一年多竟然不知道這件事,汗````,當然也是可能這件事本身和我思考方向是反着的,所以之前從來沒有出現類似的問題。

  具體來說就是,這段代碼會報錯:

#include<iostream>
using namespace std;

int main(){
    halfpoint();
    return 0;
}
void halfpoint(){
    cout<<"hello"<<endl;
}

  而這段則不會

#include<iostream>
using namespace std;
void halfpoint(){
    cout<<"hello"<<endl;
}
int main(){
    halfpoint();
    return 0;
}

  解決的方法還有先聲明:

#include<iostream>
using namespace std;
void halfpoint();
int main(){
    halfpoint();
    return 0;
}
void halfpoint(){
    cout<<"hello"<<endl;
}

這個問題在flex規則的編寫時也有體現,比如下面的代碼

{%
void yyerror(char *s);
%}
<comment><<EOF>>    {yyerror("EOF in comment");
yyterminate();}
%%
void yyerror(char *s){
printf("#%d ERROR \"%s\"\n",curr_lineno,&(*s));
return;}

當初一直好奇為什么必須先加一個對yyerror的定義,后來明白是因為在處理EOF錯誤時用到了這個函數,但是還沒有實現。

為什么要先聲明呢?我查閱了一些他人的回答,總結了一下:

It should be considered an error. But C is an ancient language, so it's only a warning.
Compiling with -Werror (gcc) fixes this problem.

When C doesn't find a declaration, it assumes this implicit declaration: int f();, which means the function can receive whatever you give it, and returns an integer. If this happens to be close enough (and in case of printf, it is), then things can work. In some cases (e.g. the function actually returns a pointer, and pointers are larger than ints), it may cause real trouble.

Note that this was fixed in newer C standards (C99, C11). In these standards, this is an error. However, gcc doesn't implement these standards by default, so you still get the warning.
如果不先聲明的話,當調用該函數時,編譯器發現一個不認識的函數調用,不知道該函數的返回類型,就假設為int類型,等后面編譯的時候編譯器看到實際的函數,它認為有兩個同名的函數,一個是文件中的函數,一個是編譯器假設返回int的那個。為了防止編譯器假設函數的返回類型,你可以顯式地告訴它。告訴編譯器函數會返回什么類型的語句就叫函數聲明。
函數聲明給出了函數名、返回值類型、參數列表(重點是參數類型)等與該函數有關的信息,稱為函數原型(Function Prototype)。函數原型的作用是告訴編譯器(1)函數的返回值(2)參數的類型,參數的個數,方便編譯器來初步排查錯誤。
其實有時候,我們沒有刻意的寫函數原型,只是將函數的實現放置在了調用前面,這其實是相當於實現和聲明的結合。
初學者編寫的代碼都比較簡單,頂多幾百行,完全可以放在一個源文件中。 對於單個源文件的程序,通常是將函數定義放到 main() 的后面,將函數聲明放到 main() 的前面,這樣就使得代碼結構清晰明了,主次分明。

使用者往往只關心函數的功能和函數的調用形式,很少關心函數的實現細節, 將函數定義放在最后,就是盡量屏蔽不重要的信息,凸顯關鍵的信息。將函數聲明放到 main() 的前面,在定義函數時也不用關注它們的調用順序了,哪個函數先定義,哪個函數后定義,都無所謂了。

然而在實際開發中,往往都是幾千行、上萬行、百萬行的代碼,將這些代碼都放在一個源文件中簡直是災難,不但檢索麻煩,而且打開文件也很慢,所以必須將這些代碼分散到多個文件中。 對於多個文件的程序,通常是將函數定義放到源文件(.c文件)中,將函數的聲明放到頭文件(.h文件)中,使用函數時引入對應的頭文件就可以,編譯器會在鏈接階段找到函數體。

前面我們在使用 printf()、puts()、scanf() 等函數時引入了 stdio.h 頭文件,很多初學者認為 stdio.h 中包含了函數定義(也就是函數體),只要有了頭文件就能運行,其實不然, 頭文件中包含的都是函數聲明,而不是函數定義,函數定義都放在了其它的源文件中,這些源文件已經提前編譯好了,並以動態鏈接庫或者靜態鏈接庫的形式存在,只有頭文件沒有系統庫的話,在鏈接階段就會報錯,程序根本不能運行。

除了函數,變量也有定義和聲明之分。實際開發過程中,變量定義需要放在源文件( .c文件)中,變量聲明需要放在頭文件( .h文件)中,在鏈接程序時會將它們對應起來
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM