C++学习笔记5[函数]

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



一、函数概述

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++中的函数。
在这里插入图片描述

作者:电气-余登武

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总裁余(余登武)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值