C++函数, since 2020-11-26

(2020.11.26 Thur)

函数的定义、声明和调用

格式如下

<函数返回类型> <函数名> (<形式参数表>) /* 函数头 /
{
expressions;
} /
函数主体 */

  • 函数返回类型顾名思义是返回值的类型,有的情况不返回值,其类型关键字是void。如果定义函数时没有明确指定类型,则默认类型是int。
  • 函数名,尽量避免下划线开头,因编译器常常定义一些下划线开头的变量或函数。
  • 形式参数表,可以有多个形式参数,也可以没有,故函数分为两类根据形参,即有参函数和无参函数。
声明

如果在使用函数前没有进行定义,则必须对其声明。函数原型声明有函数名称、类型和参数,一般格式为

[<属性说明>] <函数返回类型> <函数名>(<形式参数>);

  • 属性说明,可以默认,一般是inline(内联函数)、static(静态函数)、virtual(虚函数)、friend(友元函数)等。
  • 其余各项的要求与函数定义相似,除了第一行结尾加了;。
  • 声明语句里,形参 变量名可省略,只标注形参类型,如下面两个语句等价
double func(double x, int y, float z);
double func(double, int, float);
  • 函数声明中的形参名和函数定义中的形参名可以不同
double func(double x, int y, float z); //声明
...
double func(double a, int b, float c) //定义
{
    ...
}
调用
double func(int, float) { //定义
...
}
...
x = func(a, b); //调用,a和b称为实际参数
  • 使用库函数时,需要将库函数所对应的头文件引入,需要使用预编译指令#include<...>,而如果调用的是用户自定义函数,预编译指令为#include "..."

函数参数传递机制问题,本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信方法的问题。根据参数传递方式,函数调用分成两种方式,按值传递地址传递或引用传递

按值传递

被调函数的形式参数作为被调函数的局部变量处理,即在对战中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为实参的一个副本。其特点是被调函数以形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。其过程如下

  1. 计算出实际参数表达式的值,接着给对应的形参变量分配一个存储空间,该空间的大小等于该形参类型的长度;
  2. 把已求出的实参表达式的值一一存入到为形参变量分配的存储空间中,成为形参变量的初值,供被调函数执行时使用。

这种调用方式的被调函数本身不对实参进行操作,也就是即使形参值在函数中发生了变化,实参值也不受影响,仍为调用前的值

引用调用和指针调用

传值调用的特点是形参值的改变不能对实参产生影响,因此在有些地方不适用,比如交换两个数的值,无法用传值调用实现。

引用调用过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但其存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何惭怍都被处理成间接寻址,也就是通过堆栈中存放的地址访问主调函数中的实参变量。因此,被调函数对形参做的任何操作都影响了主调函数中的实参变量 。

引用调用方式是在函数定义时在形参面前加上引用运算符&,在调用时,参数传递的内容不是实参的值,而是实参的地址,即将实参的地址放倒C++为形参分配的内存空间中,因此对形参的任何操作都会改变实参的值。

#include <iostream.h>
void swap(int &a, int &b);
void main() {
    int x = .. , y=.. ;
    swap(x,y);
}
void swap(int &a, int &b) {
    t=a;
    a=b;
    b=t;
}

可以注意到,除了在声明和定义中的形参加了符号&,其他部分和传值调用一样。加了符号&表示该参数被调用时采用的是引用调用,传递给函数的是实参的地址,所以实现了交换的功能。

由于传递的是地址,在调用函数时不创建新的参数变量(即开辟新的内存空间),因此在程序中对占用内存较多的数据参数,为了节省内存,可采用引用传递的方式。

引用传递的另一种形式是指针传递,定义函数时将形参说明成指针,而调用函数时就需要制定地址值形式的实参。

#include <iostream>
void swap(int *a, int *b);
...
int x = .. , y = .. ;
swap(&x, &y);
...
void swap(int *a, int *b){
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}
带默认形参的调用

当一个函数既有声明也有定义时,默认参数值必须在声明中给出,而不能在定义中给出;当函数只有定义时,定义中给出。默认值的定义遵循从右到左的原则,也就是形参列表中如果一个形参没有默认值,则它左边的变量不能有默认值。

void afunc(int a, float b, char c = "this is a function", float d = 11.26);
void afunc(int a , float b, char c, float d){...}

main函数

每个C++程序都必须有一个唯一的main()函数,称为主函数。main函数中可调用其他函数,负责总控。main函数的最简单形式

main() {
变量声明语句;
执行语句;
}

在main函数中被调用的函数需要在main函数前声明或定义,至少要声明。

main函数带参数

main函数可以带参数,允许携带两个参数,一个是argc,int类型;另一个是指向字符型的指针数组argv[ ]。argc表示命令行中字符串的个数,argv[ ]指向命令行中的各个字符串。其他名字也可以,但习惯上用这两个变量名。main函数一般能在调用时追加参数。声明格式为

int main(int argc, char *argv[ ])

一个demo

#include <iostream.h>
int main(int argc, char *argv[ ]) 
{
    cout << 'the program file is: ' << argv[0] <<endl; //输出信息,argv[0]输出程序的当前路径
    for (int k = 1; k < argc; k++)
        cout << 'argv[' << k<< "]=" << argv[k] << ' '; //循环输出第二个参数中的信息
    court << endl;
    return 0;
}

变量的作用域

作用域即变量的作用范围。一个程序将操作系统分配给其运行的内存块分为4个区域

  • 代码区,存放程序的代码,即程序中的各个函数中的代码块
  • 全局数据区,存放程序全局数据和静态数据
  • 堆区,存放程序的动态数据
  • 栈区,存放程序的局部数据,即各个函数中的数据

根据变量的作用域可将变量分为局部变量和全局变量

局部变量

在一个函数内部说明的变量时内部变量,其只在该函数范围内有效,在函数外这个变量就无效了。因此这些内部变量被称为局部变量。

注意,

  • 形参变量也是局部变量,属于被调用函数;实参变量,则是调用函数的内部变量
  • 不同的函数中可以使用相同的变量名
  • 符合语句中可定义变量,作用域只在复合语句范围内
全局变量

又称外部变量,在函数外部定义的变量。不属于任何一个函数,可被作用域内所有函数直接引用,其作用域从外部变量的定义位置开始,到本文件结束为止。

#include <iostream.h>
int s1, s2; //全局变量,不需要写global
int vs(int a, int b) 
{
    s1 = a *b; //函数之前已经定义全局变量,在函数中调用时直接调用,不需要global
    s2 = a+ b;
    return 0;
}
void main() 
{
    int c = 1, d = 3;
    k = vs(c,d);
    cout<<'s1 = '<<s1<<endl;
    cout<<'s2 = '<<s2<<endl;
}
  • 同一个源文件中,允许外部变量和内部变量同名。在内部变量的作用域内,外部变量被屏蔽而不起作用
  • 全局变量的作用域是从定义点到本文件结束。如果定义点之前的函数需要引用这些外部变量时,需要在函数内对被引用的外部变量做声明:

extern 数据类型 外部变量1[,外部变量2...];

也就是说extern用于引用尚未声明的外部变量,或在全局变量声明前引用。

函数的作用域

函数作用域的概念跟变量的存储位置和生命期有关。函数的参数和在函数中声明并定义的变量即局部变量,其被分配在堆栈上,随着函数的执行而生成,随着函数的退出而消亡。

标号是唯一具有函数作用域的标识符,goto语句使用标号。标号声明使得该标识符在一个函数内的任何位置都可以被使用。

#include <iostream.h>
void fn() 
{
    goto S;
    int b;
    cin>>b;
    if (b>0)
        {
S:
         goto End;
         }
End:
        cout<<'Cannot input b '<<endl;
}
void main()
{
    fn();
}

函数重载

函数重载是指同一个函数名可以对应着多个函数的实现。每一类实现对应着一个函数体,名字相同,但是函数的参数的类型不同,这就是重载。

函数重载又称函数的多态性,所谓的不同实现,指的是这些函数的形参表必须互不相同,或者是形参的个数不同,或者形参的类型不同,或者都不相同,否则无法实现函数重载。下面的是合法的重载

int func(int, int);
int func(int);
double func(int, long);
double func(long); //合法

重载函数的(返回)类型,可以相同也可以不同。但如果仅仅是返回类型不同而函数名相同、形参表也相同,是不合法的,编译器会给出语法错误提示,下面的重载不合法

int func(int a, float b);
double func(int a, float b); //不合法

参数类型和个数不同的重载

参数类型不同的重载case

#include <iostream.h>
int add(int, int);
double add(double, double);
void main() {
    cout<<add(1, 2)<<endl; //调用第一个int add函数
    cout<<add(1.1, 2.2)<<endl; //调用第二个double add函数
}
int add(int x,int y) 
{ 
    return x+y;
}
double add(double x, double y)
{
    return x+y;
}

参数个数不同的重载case

#include <iostream.h>
int min(int, int); //两个参数的函数
int min(int, int, int);//三个参数的函数
void main() 
{
    cout<<min(2,3)<<endl; 
    cout<<min(2,3,4)<<endl;
}
int min(int a, int b) 
{
    return min(a,b);
}
int min(int a, int b, int c)
{
    t = min(a,b);
    return min(t,c);
}

函数的重载在类和对象中应用的比较多,特别是在类的多态性中。

Reference

1 刘蕾编著,21天学通C++(第五版),电子工业出版社
2 聚慕课教育研发中心 编著,C++从入门到项目实践(超值版),清华大学出版社

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容