C++学习目录链接:
C++学习笔记目录链接(持续更新中)


文章目录

  • 一、函数概述
    • 1.函数的定义
    • 2.函数的声明
  • 二、函数参数及其返回值
    • 1.返回值
    • 2.空函数
    • 3.形参和实参
    • 4.默认参数
    • 5.可变参数
  • 三、函数调用
    • 1.传值调用
    • 2.嵌套调用
    • 3.递归调用
  • 四、变量作用域
  • 五、重载函数
  • 六、内联函数
  • 七、变量的存储类别
    • 1.auto变量
    • 2.static变量
    • 3.register变量
    • 4.extern变量
  • 总结

一、函数概述

1.函数的定义

函数就是能够实现特定功能的程序模块,它可以是只有一条语句的简单函数, 也可以是包含许多子函数的复杂函数;函数有别人写好的存放在库里的库函数,也有开发人员自己写的自定义函数:函数根据功能可以分为字符函数、日期函数、数学函数、图形函数、内存函数等。 一个程序可以只有一个主函数,但不可以没有函数。

函数的定义形式一般如下:

类型标识符   函数名(形式参数列表)
{
变量的声明
语句
}

类型标识符:用来标识函数的返回值类型,可以根据函数的返回值判断函数的执行情况,通过返回值也可以获取想要的数据。类型标识符可以是整型、字符型、指针型、对象的数据类型。
    形式参数列表:由各种类型变量组成的列表,各参数之间用逗号间隔,在进行函数调用时,主调函数对变量进行赋值。
    形式参数列表可以为空,这样就定义了不需要参数的函数。例如:

#include <iostream>
using namespace std;//函数的定义int ShowMessage()
{int i=0;cout<<"i="<<i<<endl;return 0;
}
//主函数void main()
{int j;j=ShowMessage();//函数的调用,因为函数定义在调用前,故可以不声明cout<<"j="<<j<<endl;
}

2.函数的声明

调用一个函数前必须先声明函数的返回值类型和参数类型。例如:

int SetIndex(int i);

函数声明被称为函数原型,函数声明时可以省略变量名。例如:

;int SetIndex(int);

下面通过实例来介绍函数的定义、声明、调用
    下面两种代码都可以执行,可以看出,如果函数定义放在主函数前面,则函数可以不声明,直接调用。如果函数定义在主函数后面,则函数调用前必须声明。 为了避免错误,建议调用函数前都先声明下。

#include <iostream>
using namespace std;void ShowMessage();//函数声明//主函数
void main()
{ShowMessage();//函数调用
}//函数定义语句
void ShowMessage()
{cout<<"yudengwu"<<endl;
}
#include <iostream>
using namespace std;//函数定义语句
void ShowMessage()
{cout<<"yudengwu"<<endl;
}//主函数
void main()
{ShowMessage();//函数调用
}

二、函数参数及其返回值

1.返回值

函数的返回值是指函数被调用之后,执行函数体中的程序段所取得的并返回给主调函数的值,函数的返回值通过returm语句返回给主调函数。return 语句的一般形式如下:

return(表达式);

关于返回值的说明:

  • (1)函数返回值的类型和函数定义中函数的类型标识符应保持一致。 如果两者不一致,则以函数类型为准,自动进行类型转换。
  • (2)如函数值为整型,在函数定义时可以省去类型标识符。
  • (3)在函数中允许有多个retur语句,但每次调用只能有一个returm 语句被执行,因此只能返回一个函数值。
  • (4)不返回函数值的函数,可以明确定义为“空类型”,类型标识符为“void”。例如:
#include <iostream>
using namespace std;void ShowMessage();//函数声明//主函数
void main()
{ShowMessage();//函数调用
}//函数定义语句
void ShowMessage()
{cout<<"yudengwu"<<endl;
}
  • (5)类型标识符为void的函数不能进行赋值运算及值传递。例如:
    i= Showlndex();//不能进行赋值
    SetIndex(ShowIndex); //不 能进行值传递

2.空函数

没有参数和返回值,函数的作用域也为空的函数就是空函数。void setWorkSpace(){}

调用此函数时,什么工作也不做,没有任何实际意义。在主函数main 函数中调用setWorkSpace函数时,这个函数没有起到任何作用。例如: .
void setWorkSpace({ }
void main()
setWorkSpace();

空函数存在的意义是:在程序设计中往往根据需要确定若干模块,分别由一些函数来实现。 而在第一阶段只设计最基本的模块,其他一些次要功能或锦上添花的功能则在以后需要时陆续补上。在编写程序的开始阶段,可以在将来准备扩充功能的地方写上-个空函数,这些函数没有开发完成,先占一个位置,以后用一个编好的函数代替它。这样做可以使程序的结构清楚,可读性好,以后扩充新功能方便,对程序结构影响不大。

3.形参和实参

函数定义时如果参数列表为空,说明函数是无参函数;如果参数列表不为空,就称为有参函数。有参函数中的参数在函数声明和定义时被称为“形式参数”(简称形参),在函数被调用时被赋予具体值,具体的值被称为“实际参数”(简称实参)。。
    形参与实参的区别:

  • (1)在定义函数中指定的形参,在未出现函数调用时,它们并不占用内存中的存储单元。只有在发生函数调用时,函数的形参才被分配内存单元,在调用结束后,形参所占的内存单元也被释放。
  • (2)实参应该是确定的值。在调用时将实参的值赋给形参,如果形参是指针类型,就将地址值传递给形参。
  • (3)实参与形参的类型应相同。
  • (4)实参与形参之间是单项传递,只能由实参传递给形参,而不能由形参传回来给实参。
  • 实参与形参之间存在一个分配空间和参数值传递的过程,这个过程是在函数调用时发生的。C++支持引用型变量,引用型变量则没有值传递的过程,这将在后文讲到。

4.默认参数

在调用有参函数时,如果经常需要传递同一个值到调用函数,在定义函数时,可以为参数设置一个默认值,这样在调用函数时可以省略一些参数,此时程序将采用默认值作为函数的实际参数。
方式1

#include <iostream>
using namespace std;void ShowMessage(const char* pchData="yudengwu");//函数声明,声明时可以传递默认参数
//主函数
void main()
{ShowMessage();//函数调用ShowMessage("yudengwu123");//没有使用默认参数
}//函数定义语句
void ShowMessage(const char* pchData)
{cout<<pchData<<endl;
}

方式2

#include <iostream>
using namespace std;//函数定义语句
void ShowMessage(const char* pchData="yudengwu")
{cout<<pchData<<endl;
}
//主函数
void main()
{ShowMessage();//函数调用,使用默认参数ShowMessage("yudengwu123");//没有使用默认参数}

在定义函数默认参数时,如果函数具有多个参数,应保证默认参数出现在参数列表的右方,没有默认值的参数出现在参数列表的
左方,即默认参数不能出现在非默认参数的左方。

5.可变参数

库函数printf就是一一个可变参数函数,它的参数列表会显示“…”。 printf 函数原型格式如下:_CRTIMP int cdecl printf(const char*,…),“…”代表的含义是函数的参数是不固定的,可以传递一个或多个参数。对于printf函数来说,可以输出一项信息,也可以同时输出多项信息。例如:

printf("%d and % c",a,b);

声明可变参数的函数和声明普通函数一样,只是参数列表中有一个“…”例如:

void OutputInfo(int num…)//定义可变参数函数

对于可变参数的函数,在定义函数时需要- – 读取用户 传递的实际参数。可以使用va_ list 类型和va_ start、 va_ arg、 va_ end 3个宏读取传递到函数中的参数值。使用可变参数需要引用STDARGH头文件。下面以一个具体的实例介绍可变参数函数的定义及使用。

标准宏

//可变参数标准宏头文件
#include "stdarg.h"//申明va_list数据类型变量pvar,该变量访问变长参数列表中的参数。
va_list pvar;//宏va_start初始化变长参数列表。pvar是va_list型变量,记载列表中的参数信息。
//parmN是省略号"..."前的一个参数名,va_start根据此参数,判断参数列表的起始位置。
va_start(pvar, parmN);//获取变长参数列表中参数的值。pvar是va_list型变量,type为参数值的类型,也是宏va_arg返回数值的类型。
//宏va_arg执行完毕后自动更改对象pvar,将其指向下一个参数。
va_arg(pvar, type);//关闭本次对变长参数列表的访问。
va_end(pvar);

模板

#include <stdarg.h>
function (parmN, ...)
{va_list pvar; va_start (pvar, parmN);while(...){...f = va_arg (pvar, type);...}va_end (pvar);
}

实例

#include <iostream>
#include "stdarg.h"
using namespace std;int sum(int count, ...)
{int sum_value=0;va_list args;va_start(args,count);while(count--){sum_value+=va_arg(args,int);}va_end(args);return sum_value;
}int main()
{cout<<sum(5,1,2,3,4,5);//输出15,第一个5表示个数
}

三、函数调用

声明完函数后就需要在源代码中调用该函数。整个函数的调用过程被称为函数调用。标准C++是一种强制类型检查的语言,在调用函数前,必须把函数的参数类型和返回值类型告知编译。函数调用的一些说明:

  • (1)首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。
  • (2)如果使用库函数,还需要将库函数对应的头文件引入,这需要使用预编译指令#include。
  • (3)如果使用用户自定义函数,一般还应该在主调函数中对被调用的函数作声明。

1.传值调用

主调函数和被调用函数之间有数据传递关系,换句话说,主调函数将实参数值复制给被调用函数的形参处,这种调用方式被称为传值调用。如果传递的实参是结构体对象,值传递方式的效率是低下的,可以通过传指针或使用变量的引用来替换传值调用。传值调用是函数调用的基本方式。

#include <iostream>
using namespace std;
void swap(int a,int b)
{int temp;temp=a;a=b;b=temp;
}void main()
{int a=1,b=2;if (a<b){swap(a,b);}cout<<a<<endl;cout<<b<<endl;}

程序本意是想实现当a小于b时交换a和b的值,但结果并没有实现,主要原因是调用swap函数时复制了变量a和b的值,而并非变量本身。

应用指针交换

#include <iostream>
using namespace std;
void swap(int *a,int *b)
{int tmp;tmp=*a;*a=*b;*b=tmp;
}void main()
{int a=1,b=2;if (a<b){swap(a,b);}cout<<a<<endl;cout<<b<<endl;}

函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,在函数调用过程中,形参的值发生改变,实参的值不会发生变化。

2.嵌套调用

在自定义函数中调用其他自定义函数,这种调用方式称为嵌套调用。例如:

#include <iostream>
using namespace std;
void showMessage()
{cout<<"余登武"<<endl;
}
void display()
{showMessage();
}void main()
{display();
}

在函数嵌套调用时要注意,不要在函数体内定义函数,如下代码便是错误的:

#include <iostream>
using namespace std;
void showMessage()
{cout<<"余登武"<<endl;
}void main()
{void display()
{showMessage();
}
}

3.递归调用

直接或间接调用自己的函数被称为递归函数(recursive funciton)。使用递归方法解决问题的特点是:问题描述清楚、代码可读性强、结构清晰,代码量比使用非递归方法少。缺点是递归程序的运行效率比较低,无论是从时间角度还是从空间角度都比非递归程序差。对于时间复杂度和空间复杂度要求较高的程序,使用递归函数调用要慎重。递归函数必须定义一个停 止条件,否则函数将永远递归下去。

递归求和

#include <iostream>
using namespace std;int cal_sum(const int n)
{if (n == 1){return 1;}return n + cal_sum(n - 1);
}int main()
{int num = 0;cin >> num;int sum = 0;sum = cal_sum(num);cout << "the sum is " << sum << endl;system("pause");return 0;
}

四、变量作用域

根据变量声明的位置可以将变量分为局部变量及全局变量,在函数体内定义的变量称为局部变量,在函数体外定义的变量称为全局变量。例如:


#include <iostream>
using namespace std;int iTotalCount;//全局变量int GetCount(); //函数声明void main()
{int iTotalCount=100;//局部变量cout<<iTotalCount<<endl;cout<<GetCount()<<endl;//函数调用}//函数定义
int GetCount()
{iTotalCount=200;return iTotalCount;
}

变量都有它的生命期,全局变量在程序开始时创建并分配空间,在程序结束时释放内存并销毁;局部变量是在函数调用时创建,并在栈中分配内存,在函数调用结束后销毁并释放。

五、重载函数

定义同名的变量,程序会编译出错,定义同名的函数也会带来冲突的问题,但C++中使用了名字重组的技术,通过函数的参数类型来识别函数,所谓重载函数就是指多个函数具有相同的函数标识符,
但参数类型或参数个数不同。函数调用时,编译器以参数的类型及个数来区分调用哪个函数。下面的实例定义了重载函数。

#include <iostream>
using namespace std;//定义第一个重载函数
int Add(int x,int y)
{return x+y;
}//定义第二个重载函数double Add(double x,double y)
{return x+y;
}void main()
{int Arr=Add(5,2);//第一个重载函数调用cout<<Arr<<endl;float Arr1=Add(5.2,2.1);//第二个重载函数调用cout<<Arr1<<endl;
}

    在定义重载函数时,应注意函数的返回值类型不作为区分重载函数的一部分。下面的函数重载是 非法的。

//定义第一个重载函数
int Add(int x,int y)
{return x+y;
}//定义第二个重载函数double Add(int x,int y)
{return x+y;
}

六、内联函数

通过inline关键字可以把函数定义为内联函数,编译器会在每个调用该函数的地方展开一个函数的副本。

#include <iostream>
using namespace std;//定义一个内敛函数
inline int Add(int x,int y)
{return x+y;
}void main()
{int Arr=Add(5,2);//调用cout<<Arr<<endl;}

上面的执行代码等于:

#include <iostream>
using namespace std;//定义一个内敛函数
inline int Add(int x,int y)
{return x+y;
}void main()
{int a=5;int b=2;int Arr=a+b;cout<<Arr<<endl;}

使用内联函数可以减少函数调用带来的开销(在程序所在文件内移动指针寻找调用函数地址带来的开销),但它只是一种解决方案,编译器可以忽略内联的声明。
    应该在函数实现代码很简短或者调用该函数次数相对较少的情况下将函数定义为内联函数,一个递归函数不能在调用点完全展开,一个一千行代码的函数也不大可能在调用点展开,内联函数只能在
优化程序时使用。在抽象数据类设计中,内联函数对支持信息隐藏起着主要作用。
    如果某个内联函数要作为外部全局函数,即它将被多个源代码文件使用,那么就把它定义在头文件里,在每个调用该内联函数的源文件中包含该头文件,这种方法保证对每个内联函数只有一个定义,防止在程序的生命期中引起无意的不匹配。

七、变量的存储类别

存储类别是变量的属性之一,C++语言中定义了4种变量的存储类别,分别是auto变量、static 变量、register 变量和exterm变量。变量存储方式不同会使变量的生存期不同,生存期表示了变量存在的时间。生存期和变量作用域是从时间和空间这两个不同的角度来描述变量的特性。
    静态存储变量通常是在变量定义时就分配固定的存储单元并一直保持不变,直至整个程序结束。前面讲过的全局变量即属于此类存储方式,它们存放在静态存储区中。动态存储变量是在程序执行过程中使用它时才分配存储单元,使用完毕立即将该存储单元释放。前面讲过的函数的形式参数,在函数定义时并不给形参分配存储单元,只是在函数被调用时才予以分配,调用函数完毕立即释放,此类变量存放在动态存储区中。从以上分析可知,静态存储变量是一直存在的,而动态存储变量则时而存在时而消失。

1.auto变量

这种存储类型是C ++语言程序中默认的存储类型。函数内未加存储类型说明的变量均视为自动变量,也就是说自动变量可省去关键字auto。例如:

int a,b,c;

等价于

auto int a,b,c;

自动变量有如下特点:

  • (1)自动变量的作用域仅限于定义该变量的个体内。在函数中定义的自动变量,只在该函数内有效;在复合语句中定义的自动变量,只在该复合语句中有效。
  • (2)自动变量属于动态存储方式,变量分配的内存是在栈中,当函数调用结束后,自动变量的值会被释放。同样,在复合语句中定义的自动变量,在退出复合语句后也不能再使用,否则将引起错误。
  • (3)由于自动变量的作用域和生存期都局限于定义它的个体内(函数或复合语句内),因此不同的个体中允许使用同名的变量而不会混淆。即使在函数内定义的自动变量也可与该函数内部的复合语句中定义的自动变量同名。

2.static变量

在声明变量时加关键字static,可以将变量声明为静态变量。静态局部变量的值在函数调用结束后不消失,静态全局变量只能在本源文件中使用。例如下面的代码声明变量为静态变量:

static int a,b;
static float c,d;

静态变量属于静态存储方式,它具有以下特点:

  • (1)静态变量在函数内定义,在程序退出时释放,在程序整个运行期间都不释放,也就是说它的生存期为整个源程序。
  • (2)静态变量的作用域与自动变量相同,在函数内定义就在函数内使用,尽管该变量还继续存在,但不能使用它,如再次调用定义它的函数时,它又可继续使用。
  • (3)编译器会为静态局部变量赋予0值。
/*求累加和。*/#include <iostream>
using namespace std;int Add(int x)
{static int n=0;//定义n为static int 型变量
n=n+x;
return n;
}void main()
{int i,j,sum;cout<<"请输入一个数:"<<endl;cin>>j;cout<<"输入的数为:"<<j<<endl;for(i=0;i<=j;i++){sum=Add(i);cout<<"sum is:"<<sum<<endl;}
}

如果去掉static

/*求累加和。*/#include <iostream>
using namespace std;int Add(int x)
{ int n=0;//定义n为int 型变量
n=n+x;
return n;
}void main()
{int i,j,sum;cout<<"请输入一个数:"<<endl;cin>>j;cout<<"输入的数为:"<<j<<endl;for(i=0;i<=j;i++){sum=Add(i);cout<<"sum is:"<<sum<<endl;}
}

3.register变量

通常变量的值存放在内存中,当对一个变量频繁读写时,需要反复访问内存储器,此时将花费大量的存取时间。为了提高效率,C++语言 可以将变量声明为寄存器变量,这种变量将局部变量的值存
放在CPU中的寄存器中,使用时不需要访问内存,而直接从寄存器中读写。寄存器变量的声明符是register.
对寄存器变量的说明:

  • (1)寄存器变量属于动态存储方式。凡需要采用静态存储方式的变量不能定义为寄存器变量。
  • (2)编译程序会自动决定哪个变量使用寄存器存储。register 可以起到程序优化的作用。

4.extern变量

在-一个源文件中定义的变量和函数只能被本源文件中的函数调用,一个C++程序中会有许多源文件,那么如何使用非本源文件中的全局变量呢? C++t提供了exterm 关键字来解决这个问题。在使用其他源文件中的全局变量时,只需要在本源文件中使用exterm关键字来声明这个变量即可。例如,在3.cpp源文件中定义全局变量a、b、c, 代码如下:

#include <iostream>using namespace std;int a,b;//外部变量定义
char c;//外部变量定义void main()
{cout<<a<<endl;
cout<<b<<endl;
cout<<c<<endl;
}

在4.cpp源文件中要使用3 .cpp源文件中的全局变量a、b、c,
代码如下:

#include <iostream>
using namespace std;
extern int a,b;//外部变量说明
extern char c;//外部变量说明void main()
{cout<<a<<endl;
cout<<b<<endl;
cout<<c<<endl;
}

3.cpp,4.cpp的运行结果都为如图


总结

本文讲解了C++中的函数。

作者:电气-余登武

C++学习笔记5[函数]相关推荐

  1. python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用

    本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...

  2. Python学习笔记:函数(Function)

    Python学习笔记:函数(Function) 一.函数基本概念 函数是Python里组织与重用代码最重要的方法.一般来说,如果你期望多次重复相同或相似的代码,写一个可重用的函数可能是值得的.函数通过 ...

  3. php中声明一个函数,php学习笔记之 函数声明

    /* 函数定义: * 1.函数是一个被命名的 * 2.独立的代码段 * 3.函数执行特定任务 * 4.并可以给调用它的程序返回一个值 * * 函数的优点: * 1.提高程序的重用性 * 2.提高程序的 ...

  4. Matlab学习笔记 figure函数

    Matlab学习笔记 figure函数 matlab中的 figure 命令,能够创建一个用来显示图形输出的一个窗口对象.每一个这样的窗口都有一些属性,例如窗口的尺寸.位置,等等.下面一一介绍它们. ...

  5. JAVA学习笔记五---函数

    JAVA学习笔记五---函数 5.1 方法的学习 编写一个程序,求圆的周长和面积. package practice; /*** 编写一个程序,求圆的周长和面积.* @author iszhangyo ...

  6. MySQL学习笔记—自定义函数

    MySQL学习笔记-自定义函数 注释语法: MySQL服务器支持3种注释风格: 从'#'字符从行尾. 从'– '序列到行尾.请注意'– '(双破折号)注释风格要求第2个破折号后面至少跟一个空格符(例如 ...

  7. matlab机器人工具箱学习笔记——ikine函数

    matlab机器人工具箱学习笔记--ikine函数 ikine函数用法 使用实例 链接: https://blog.csdn.net/weixin_42596724/article/details/8 ...

  8. 《JavaScript语言精粹》学习笔记(函数(2))

    <JavaScript语言精粹>学习笔记(函数(2)) 函数(Functions) 参数(Arguments) 当参数被调用时,会得到一个"免费"的参数数组argume ...

  9. matlab 调用子函数返回值,matlab学习笔记13_1 函数返回值

    一起来学matlab-matlab学习笔记13函数 13_1 函数返回值 觉得有用的话,欢迎一起讨论相互学习~Follow Me 函数返回一个值 返回值不必使用return语句,而是直接将需要返回的变 ...

  10. Python学习笔记12_函数

    Python学习笔记12_函数 文章目录 Python学习笔记12_函数 1.函数定义 2.函数调用 3.函数的参数 3.1.可更改对象和不可更改对象参数 3.2.必需参数(位置参数) 3.3.关键字 ...

最新文章

  1. linux运维实战练习-2016年3月4日-3月19日课程作业(练习)安排
  2. 软银宣布启动5G Project:全球首家商用Massive MIMO技术
  3. PHP 连接 Rabbitmq 实例代码(亲测通过)
  4. RabbitMQ (五) 订阅者模式之分发模式 ( fanout )
  5. C++图解前缀树(字典树)
  6. Apache+Tomcat集群负载均衡的两种session处理方式
  7. 单条MySQL最长_MySQL 单条记录长度最大65535
  8. 浅说深度学习(2):简史
  9. JAVA类与对象(一)----基础概念理解
  10. REST和RESTful详解到实战
  11. 解决开ServiceHost时候System.PlatformNotSupportedException: Operation is not supported on this platform.
  12. 谈谈我自己为什么突然想写技术博客
  13. RPC与Apache Thift
  14. SharePoint 2013 创建web应用程序报错This page can’t be displayed
  15. 图像和视频语义分割的深度学习技术综述
  16. 使用JMeter进行简单的app接口测试
  17. 医疗行业用户容灾备份方案
  18. 智能LED电子钟的制作
  19. rock带你读CornerNet-lite系列源码(二)
  20. 用JSP/Servlet应用开发一个简单的考试报名系统

热门文章

  1. 【如何利用idea提交本地代码到git远程仓库,史上最详细教程,建议收藏!】
  2. Spring-Bean依赖注入(引用数据类型和集合数据类型)
  3. 性能测试——美团国内机票网站(Badboy、JMeter)
  4. Nearest Common Ancestors
  5. Applese 填数字
  6. Theatre Square
  7. c语言tracert程序一直超时,traceroute – tracert命令返回超时
  8. 【过程记录】springboot中使用EhcacheCache+mybatis
  9. Python实训day06pm【网络爬虫(爬取接口)-爬取图片与数据】
  10. Android App的架构设计:从VM、MVC、MVP到MVVM