C++中函数声明与编译的关系探讨

2020-07-16 18:19高建波方芳赵敏
教育教学论坛 2020年23期

高建波 方芳 赵敏

[摘 要] 使用函数声明可以避免在主函数之前定义所有的子函数。然而,函数声明却有很多细节需要关注,尤其是遇到函数之间嵌套调用时。为深入理解函数声明,必须清楚程序编译原理和编译过程。该文通过一个C++例题的分析,详细阐述函数声明与编译的关系,同时说明养成良好编程习惯的重要性。

[关键词] 函数声明;编译;编程习惯

[作者简介] 高建波,南昌航空大学信息工程学院讲师,主要从事计算机科学与技术研究;方 芳,南昌航空大学信息工程学院讲师,主要从事计算机科学研究;赵 敏,南昌航空大学信息工程学院讲师,主要从事计算机科学研究。

[中图分类号] G642.0    [文献标识码] A    [文章编号] 1674-9324(2020)23-0135-02    [收稿日期] 2019-11-25

一般来说在编程时,子函数的定义可以放在主函数之前,也可以放在主函数之后。将子函数的声明放在主函数之前,子函数的定义放在主函数之后,可以让程序显得更加直观,这也是良好的编程习惯,而很多编程教学者和初学者并不重视这一点[1]。不好的编程习惯会导致很多错误,而这些错误在良好编程习惯下会自动屏蔽,因此有必要深入探讨函数声明,尤其是编程中遇到函数之间相互调用和嵌套调用时。为理解函数声明的重要性,需要考虑编译器的工作原理及其与函数声明的关系[2]。

一、函数声明和编译

(一)编译器和编译

编译器是C++程序的语法拼写检查器,当一个程序被写好并存储后,就可以对它进行编译。这时编译器程序就会对程序进行词法分析、语法分析、语义分析和中间代码生成。在任何一个环节发现错误编译器就会给出错误的报告清单。编译器的工作中词法分析的任务是从左到右、从上到下逐字符地读入源程序,即对构成源程序的字符进行扫描然后根据构词规则识别单词符号。语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语并判断程序在结构上是否正确。语义分析的任务是对结构上正确的源程序进行上下文有关性质的审查。接着编译程序将源程序变成中间代码,一种结构简单、含义明确、易于翻译成目标代码的的记号系统。

(二)函数声明与编译的关系

在编程时,程序员经常将主函数放在程序的最前面,这样程序看起来更加直观。然而当主函数中需要调用很多的子函数时,如果将子函数的定义均放在主函数之后,那么在程序编译时就会报错。报错的原因在于编译器在编译程序时是从上至下进行的,在调用子函数时,必须让计算机知道这个程序中存在这么一个函数,而将子函数定义在主函数之后,那么在对主函数进行编译时无法找到对应的子函数。为解决这个问题,可以在主函数之前先声明子函数的原型定义[3],以便让编译器知道函数的参数和返回值类型,在主函数之后再具体定义子函数的内容。例如,

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()函数,该函数没有形参和返回值,且在主函数的前面,程序的第8、9行定义了函数printmessage(),也即明确说明函数的功能。该程序亦可做如下的修改:将子函数printmessage()定义在主函数之前,即将程序的第8、9行放置于程序的第5行之前,并去掉程序的第4行。两种方法都可以通过编译,相比而言,第一种方法更符合良好编程习惯的要求。

二、子函数之间有嵌套调用时的函数声明

上例中,子函数可以放在主函数之前或之后,因此编程者会忽视良好编程习惯。在遇到函数之间相互嵌套调用时,对子函数声明的采用与否,则会产生完全不同的结果。

(一)一个嵌套调用的例子和相关说明

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;}

该程序的两个子函数odd()和even()之间存在相互调用的情形。虽然将两个子函数均定义在了主函数之前,然而因为编译是从上至下执行的,当执行到7行时odd()函数调用了even()函数,而此时even()函数根本不存在。同理如果将even()函数定义放在odd()定义之前依然会遇到类似问题。在以上两种情况下,该程序均无法通过编译。而良好的编程习惯事先要求声明所有子函数,从而不会产生这种编译错误。

(二)對上例的深入分析

为更加深入理解函数声明和编译之间的关系,设计了四个小程序片段,见表1。

这四个片段并不是完整的程序,却可用来说明函数声明和编译关系。在片段1中,主函数之前声明了子函数even(),由于在主函数的内容调用了odd()函数,而此时并没有声明odd()函数,编译器执行到这里时直接报错。在片段2中,函数even()中调用了odd(),而此时odd()并不存在,编译器立刻报错。在片段3中,先声明函数odd(),然后在even()中进行了调用,这是没有问题的;同理,主函数也可以调用odd()函数。片段4中先声明了函数odd(),然后主函数中调用odd(),再在even()函数中调用odd(),而函数odd()也调用了even(),因为even()定义在odd()之前,因此也没有问题。虽然片段3和4可以通过编译,然而先声明子函数的方式显得更加简单和直观,免去了分析子函数相互调用的细节。

三、结论

因为编译器在编译程序时是从上至下的,所以使用函数声明可以避免子函数间相互调用而产生的编译错误,而这也是良好的编程习惯。本文构建了四个小程序片段,用于说明函数声明和编译的关系,从而加深对函数声明及其重要性的理解。

参考文献

[1]何雪英.C++教学中编程习惯的培养[J].电脑知识,2019,15(22):126-127,129.

[2]万新燕,时招军.编译原理实验教学设计[J].教育教学论坛,2019,2(8):261-262.

[3]保罗.斯奈思(著),刘继飞等(译).C++[M].沈阳:辽宁教育出版社,2002.

Discussion on the Relation Between Function Declaration and Compilation in C++

GAO Jian-bo,FANG 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