高建波 方芳 趙敏
[摘 要] 使用函數(shù)聲明可以避免在主函數(shù)之前定義所有的子函數(shù)。然而,函數(shù)聲明卻有很多細(xì)節(jié)需要關(guān)注,尤其是遇到函數(shù)之間嵌套調(diào)用時(shí)。為深入理解函數(shù)聲明,必須清楚程序編譯原理和編譯過程。該文通過一個(gè)C++例題的分析,詳細(xì)闡述函數(shù)聲明與編譯的關(guān)系,同時(shí)說明養(yǎng)成良好編程習(xí)慣的重要性。
[關(guān)鍵詞] 函數(shù)聲明;編譯;編程習(xí)慣
[作者簡介] 高建波,南昌航空大學(xué)信息工程學(xué)院講師,主要從事計(jì)算機(jī)科學(xué)與技術(shù)研究;方 芳,南昌航空大學(xué)信息工程學(xué)院講師,主要從事計(jì)算機(jī)科學(xué)研究;趙 敏,南昌航空大學(xué)信息工程學(xué)院講師,主要從事計(jì)算機(jī)科學(xué)研究。
[中圖分類號(hào)] G642.0? ? [文獻(xiàn)標(biāo)識(shí)碼] A? ? [文章編號(hào)] 1674-9324(2020)23-0135-02? ? [收稿日期] 2019-11-25
一般來說在編程時(shí),子函數(shù)的定義可以放在主函數(shù)之前,也可以放在主函數(shù)之后。將子函數(shù)的聲明放在主函數(shù)之前,子函數(shù)的定義放在主函數(shù)之后,可以讓程序顯得更加直觀,這也是良好的編程習(xí)慣,而很多編程教學(xué)者和初學(xué)者并不重視這一點(diǎn)[1]。不好的編程習(xí)慣會(huì)導(dǎo)致很多錯(cuò)誤,而這些錯(cuò)誤在良好編程習(xí)慣下會(huì)自動(dòng)屏蔽,因此有必要深入探討函數(shù)聲明,尤其是編程中遇到函數(shù)之間相互調(diào)用和嵌套調(diào)用時(shí)。為理解函數(shù)聲明的重要性,需要考慮編譯器的工作原理及其與函數(shù)聲明的關(guān)系[2]。
一、函數(shù)聲明和編譯
(一)編譯器和編譯
編譯器是C++程序的語法拼寫檢查器,當(dāng)一個(gè)程序被寫好并存儲(chǔ)后,就可以對(duì)它進(jìn)行編譯。這時(shí)編譯器程序就會(huì)對(duì)程序進(jìn)行詞法分析、語法分析、語義分析和中間代碼生成。在任何一個(gè)環(huán)節(jié)發(fā)現(xiàn)錯(cuò)誤編譯器就會(huì)給出錯(cuò)誤的報(bào)告清單。編譯器的工作中詞法分析的任務(wù)是從左到右、從上到下逐字符地讀入源程序,即對(duì)構(gòu)成源程序的字符進(jìn)行掃描然后根據(jù)構(gòu)詞規(guī)則識(shí)別單詞符號(hào)。語法分析的任務(wù)是在詞法分析的基礎(chǔ)上將單詞序列組合成各類語法短語并判斷程序在結(jié)構(gòu)上是否正確。語義分析的任務(wù)是對(duì)結(jié)構(gòu)上正確的源程序進(jìn)行上下文有關(guān)性質(zhì)的審查。接著編譯程序?qū)⒃闯绦蜃兂芍虚g代碼,一種結(jié)構(gòu)簡單、含義明確、易于翻譯成目標(biāo)代碼的的記號(hào)系統(tǒng)。
(二)函數(shù)聲明與編譯的關(guān)系
在編程時(shí),程序員經(jīng)常將主函數(shù)放在程序的最前面,這樣程序看起來更加直觀。然而當(dāng)主函數(shù)中需要調(diào)用很多的子函數(shù)時(shí),如果將子函數(shù)的定義均放在主函數(shù)之后,那么在程序編譯時(shí)就會(huì)報(bào)錯(cuò)。報(bào)錯(cuò)的原因在于編譯器在編譯程序時(shí)是從上至下進(jìn)行的,在調(diào)用子函數(shù)時(shí),必須讓計(jì)算機(jī)知道這個(gè)程序中存在這么一個(gè)函數(shù),而將子函數(shù)定義在主函數(shù)之后,那么在對(duì)主函數(shù)進(jìn)行編譯時(shí)無法找到對(duì)應(yīng)的子函數(shù)。為解決這個(gè)問題,可以在主函數(shù)之前先聲明子函數(shù)的原型定義[3],以便讓編譯器知道函數(shù)的參數(shù)和返回值類型,在主函數(shù)之后再具體定義子函數(shù)的內(nèi)容。例如,
1.#include “stdafx.h”
2.#include
3.using namespace std;
4.void printmessage()
5.int main()
6.{ printmessage();
7.return 0;}
8.void printmessage()
9.{cout << “I'm a function!”;}
該程序第4行聲明了printmessage()函數(shù),該函數(shù)沒有形參和返回值,且在主函數(shù)的前面,程序的第8、9行定義了函數(shù)printmessage(),也即明確說明函數(shù)的功能。該程序亦可做如下的修改:將子函數(shù)printmessage()定義在主函數(shù)之前,即將程序的第8、9行放置于程序的第5行之前,并去掉程序的第4行。兩種方法都可以通過編譯,相比而言,第一種方法更符合良好編程習(xí)慣的要求。
二、子函數(shù)之間有嵌套調(diào)用時(shí)的函數(shù)聲明
上例中,子函數(shù)可以放在主函數(shù)之前或之后,因此編程者會(huì)忽視良好編程習(xí)慣。在遇到函數(shù)之間相互嵌套調(diào)用時(shí),對(duì)子函數(shù)聲明的采用與否,則會(huì)產(chǎn)生完全不同的結(jié)果。
(一)一個(gè)嵌套調(diào)用的例子和相關(guān)說明
1.#include “stdafx.h”
2.#include
3.using namespace std;
4.void odd (int a) {
5.if ((a%2)!=0) cout << “Odd number.\n”;
6.else even (a);}
7.void even (int a) {
8.if ((a%2)==0) cout << “Even number.\n”;
9.else odd (a);}
10.int main () {
11.int i;
12.do {
13.cout << “Type a number: (0 to exit)”;
14.cin >> i;
15.odd (i);
16.} while (i!=0);
17.return 0;}
該程序的兩個(gè)子函數(shù)odd()和even()之間存在相互調(diào)用的情形。雖然將兩個(gè)子函數(shù)均定義在了主函數(shù)之前,然而因?yàn)榫幾g是從上至下執(zhí)行的,當(dāng)執(zhí)行到7行時(shí)odd()函數(shù)調(diào)用了even()函數(shù),而此時(shí)even()函數(shù)根本不存在。同理如果將even()函數(shù)定義放在odd()定義之前依然會(huì)遇到類似問題。在以上兩種情況下,該程序均無法通過編譯。而良好的編程習(xí)慣事先要求聲明所有子函數(shù),從而不會(huì)產(chǎn)生這種編譯錯(cuò)誤。
(二)對(duì)上例的深入分析
為更加深入理解函數(shù)聲明和編譯之間的關(guān)系,設(shè)計(jì)了四個(gè)小程序片段,見表1。
這四個(gè)片段并不是完整的程序,卻可用來說明函數(shù)聲明和編譯關(guān)系。在片段1中,主函數(shù)之前聲明了子函數(shù)even(),由于在主函數(shù)的內(nèi)容調(diào)用了odd()函數(shù),而此時(shí)并沒有聲明odd()函數(shù),編譯器執(zhí)行到這里時(shí)直接報(bào)錯(cuò)。在片段2中,函數(shù)even()中調(diào)用了odd(),而此時(shí)odd()并不存在,編譯器立刻報(bào)錯(cuò)。在片段3中,先聲明函數(shù)odd(),然后在even()中進(jìn)行了調(diào)用,這是沒有問題的;同理,主函數(shù)也可以調(diào)用odd()函數(shù)。片段4中先聲明了函數(shù)odd(),然后主函數(shù)中調(diào)用odd(),再在even()函數(shù)中調(diào)用odd(),而函數(shù)odd()也調(diào)用了even(),因?yàn)閑ven()定義在odd()之前,因此也沒有問題。雖然片段3和4可以通過編譯,然而先聲明子函數(shù)的方式顯得更加簡單和直觀,免去了分析子函數(shù)相互調(diào)用的細(xì)節(jié)。
三、結(jié)論
因?yàn)榫幾g器在編譯程序時(shí)是從上至下的,所以使用函數(shù)聲明可以避免子函數(shù)間相互調(diào)用而產(chǎn)生的編譯錯(cuò)誤,而這也是良好的編程習(xí)慣。本文構(gòu)建了四個(gè)小程序片段,用于說明函數(shù)聲明和編譯的關(guān)系,從而加深對(duì)函數(shù)聲明及其重要性的理解。
參考文獻(xiàn)
[1]何雪英.C++教學(xué)中編程習(xí)慣的培養(yǎng)[J].電腦知識(shí),2019,15(22):126-127,129.
[2]萬新燕,時(shí)招軍.編譯原理實(shí)驗(yàn)教學(xué)設(shè)計(jì)[J].教育教學(xué)論壇,2019,2(8):261-262.
[3]保羅.斯奈思(著),劉繼飛等(譯).C++[M].沈陽:遼寧教育出版社,2002.
Discussion on the Relation Between Function Declaration and Compilation in C++
GAO Jian-bo,F(xiàn)ANG Fang,ZHAO Min
(Nanchang Institute of Information Engineering,Nanchang,Jiangxi 330000,China)
Abstract:Using function declaration can avoid defining all sub-functions before the main function.However,function declarations have many details to pay attention to,especially when it comes to nested calls between functions.In order to understand the function declaration in depth,we must make clear the principle of program compilation and the compiling process.This paper elaborates the relation between function declaration and compilation through a C++ example analysis,and explains the importance of developing good programming habits.
Key words:function declaration;compilation;programming habit