C++函数

一、函数基本知识

要使用c++函数,必须完成如下工作:

  1. 提供函数定义;
  2. 提供函数原型;
  3. 调用函数;

1.定义函数

可以将函数分成两类:没有返回值的函数和有返回值的函数。

//没有返回值的函数被称为void函数。
void functionName(parameterList//指定传参的参数类型和数量)
{
	statement(s);
	return;
}
//有返回值的函数将生成一个值,并将它返回给调用函数
typeName functionName(parameterList)
{
	statements;
	return value;
}

2.函数原型和函数调用

//函数原型:函数原型是一条语句,因此必须以分号结束。获得原型最简单的方法是:复制函数定义中的函数头,并添加分号。
double cube(double x);

//函数原型不要求提供变量名,有类型列表就足够了。对于cheers()的原型,该程序只提供了参数类型:
void cheers(int);

实例:

#include <iostream>
using namespace std;
void cheers(int);
double cube(double);
int main()
{
    cheers(5);
    cout<<"Give me a number:";
    double side;
    cin>>side;
    double volume = cube(side);
    cout<<"A"<<side<<"-foot cube has a volume of";
    cout<<volume<<"cubic feet"<<endl;
    cheers(cube(2));
    return 0;
}
void cheers(int n)
{
    for(int i=0;i<n;i++)
        cout<<"Cheers!"<<endl;
}
double cube(double x)
{
    return x*x*x;
}

在这里插入图片描述
main()使用函数名和参数来调用void类型的函数:cheers(5);这是一个函数调用语句。但由于cube()有返回值,因此main()可以将其用在赋值语句中。

double volume = cube(side);

3.函数调用

调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。如:

cheers(5);
double volume = cube(side);
4.多个参数

函数可以有多个参数,在调用函数时,只需要使用逗号将这些参数分开。

n_chars("R",25);

可以这样定义:

void n_chars(char c,int n)

实例:

#include <iostream>
using namespace std;
void n_chars(char,int);
int main()
{
    int times;
    char ch;

    cout<<"Enter a character:";
    cin>>ch;
    while(ch !='q')
    {
        cout<<"Enter an integer:";
        cin>>times;
        n_chars(ch,times);
        cout<<"\nEnter another character or press yhe q-key to quit:";
        cin>>ch;
    }
    cout<<"time is"<<times<<endl;
    return 0;
}
void n_chars(char c, int n)
{
    while(n-->0)
        cout<<c;
}

在这里插入图片描述

二、函数调用

1.传值调用

向函数传递参数的值,即把参数的值复制给函数的形式参数。
这时修改函数内的形式参数,并不会影响到函数外的实际参数,
形参不改变实参。

实例:

#include <iostream>
using namespace std;
int main()
{
    int x=10;
    int y=20;

    cout<<"x原值="<<x<<endl;
    cout<<"y原值="<<y<<endl;

    swap(x,y);
    cout<<"x后值="<<x<<endl;
    cout<<"y后值="<<y<<endl;
    return 0;
}
void swap(int a,int b)
{
    int temp;

    temp=a;
    a=b;
    b=temp;
}

在这里插入图片描述

2.指针调用

向函数传递参数的指针,即把参数的地址复制给形式参数,在函数内,该地址用于访问要用到的实际参数,这意味着修改形式参数会改变实际参数。

实例:

#include <iostream>
using namespace std;
void swap(int *a,int *b);
int main()
{
    int x=10;
    int y=20;

    cout<<"x原值="<<x<<endl;
    cout<<"y原值="<<y<<endl;

    swap(&x,&y);
    cout<<"x后值="<<x<<endl;
    cout<<"y后值="<<y<<endl;
    return 0;
}
void swap(int *a,int *b)
{
    int temp;

    temp=*a;
    *a=*b;
    *b=temp;
}

在这里插入图片描述

3.引用调用

向函数传递参数的引用,即把引用的地址复制给形式参数,在函数内,该引用用于访问要用到的实际参数,这也意味着修改形式参数会改变实际参数。

实例:

include <iostream>
using namespace std;
void swap(int &a,int &b);
int main()
{
    int x=10;
    int y=20;

    cout<<"x原值="<<x<<endl;
    cout<<"y原值="<<y<<endl;

    swap(x,y);
    cout<<"x后值="<<x<<endl;
    cout<<"y后值="<<y<<endl;
    return 0;
}
void swap(int &a,int &b)
{
    int temp;

    temp=a;
    a=b;
    b=temp;
}

在这里插入图片描述

三、函数和c-风格字符串,string对象,array对象,函数和数组,函数和结构,函数和递归

1.将c-风格字符串作为参数的函数

若要将字符串作为参数传递给函数,则表示字符串的方式有三种:

  1. char数组。

  2. 用引号括起的字符串常量。

  3. 被设置为字符串的地址的char指针。

但三种情况的类型都是char指针,因此可以用作字符串处理函数的参数:

char ghots[15]="galloping";
char * str ="galumphing";
int n1=strlen(ghots);
 int n2=strlen(str);
 int n3=strlrn("galumphing");

可以说将字符串作为参数来传递,但实际传递的是字符串第一个字符的地址。

2.返回c-风格字符串的函数

编写一个返回字符串的函数,函数无法返回一个字符串,但可以返回字符串的地址。

实例:下面程序定义一个名为buildstr()函数,该函数返回一个指针,该函数接受两个参数:字符和数字。函数使用new创建一个长度与数字参数相等的字符串,将每个元素初始化为该函数。然后,返回指向新字符串的指针·。

#include <iostream>
using namespace std;
char * buildstr(char c,int n);
int main()
{
    int times;
    char ch;
    cout<<"Enter a character:";
    cin>>ch;
    cout<<"Enter an integer:";
    cin>>times;
    char *ps=buildstr(ch,times);
    cout<<ps<<endl;
    delete [] ps;
    ps=buildstr('+',20);
    cout<<ps<<"-DONE"<<ps<<endl;
    delete [] ps;
    return 0;
}
char * buildstr(char c, int n)
{
    char * pstr=new char(n+1);
    pstr[n]='\0';
    while (n-->0)//循环n,直到n减少到0
        pstr[n]=c;
    return pstr;
}

在这里插入图片描述

3.函数和string对象

  1. C-风格字符串和string对象的用途相同,但与数组相比,string对象与结构更相似。如:可以将结构赋给结构,对象赋给对象。可以将结构作为完整的实体传递给函数,也可以将对象作为完整的实体传递。

  2. 需要多个字符串时,可以声明一个string对象数组,而不是二维char数组。

实例:下面程序它声明了一个string对象数组,并将该数组传递给一个函数:

#include <iostream>
#include<string>
using namespace std;
const int SIZE=5;
void display(const string sa[],int n);
int main()
{
    string list[SIZE];
    cout<<"Enter your "<<SIZE<<"favorite astronomical sights:"<<endl;
    for(int i=0;i<SIZE;i++)
    {
        cout<<i+1<<": ";
        getline(cin,list[i]);
    }
    cout<<"Your list:"<<endl;
    display(list,SIZE);
    return 0;
}
void display(const string sa[], int n)
{
    for(int i=0;i<n;i++)
        cout<<i+1<<": "<<sa[i]<<endl;
}

在这里插入图片描述

4.函数与array对象

在C++中,类对象是基于结构的,因此结构编程方面的有些因素也适用于类,如下面使用c++模板类array的例子,使用array对象存储一年四季的开支:

array<double,4>expenses;

//若函数来显示expenses的内容,可安值传递expenses:
show(expenses);

//修改对象expenses,则需将对象的地址传递给函数:
fill(&expenses);

实例:

#include <iostream>
#include<string>
#include<array>
using namespace std;
const int Seasons=4;
const array<string,Seasons> Snames=
{"Spring","Summer","Fall","Winter" };

void fill(array<double,Seasons>*pa);
void show(array<double,Seasons> da);

int main()
{
    array<double,Seasons>expenses;
    fill(&expenses);
    show(expenses);
    return 0;
}

void fill(array<double,Seasons>*pa)
{
    for(int i=0;i<Seasons;i++)
    {
        cout<<"Enter"<<Snames[i]<<"expenses:";
        cin>>(*pa)[i];
    }
}

void show(array<double, Seasons> da)
{
    double total=0;
    cout<<"\nEXPENSES\n";
    for(int i=0;i<Seasons;i++)
    {
        cout<<Snames[i]<<":$"<<da[i]<<endl;
        total+=da[i];
    }
    cout<<"Total Expenses:$"<<total<<endl;
}

在这里插入图片描述
注:模板array并非只能存储基本数据类型,它还可以存储类对象。上面const array对象Snames是在所有函数之前声明的,因此可以在如何函数定义中使用它。

5.函数和数组
  1. 函数是处理更复杂的类型(如数组和结构)的关键,如:int sum_arr(int arr[],int n)
  2. 方括号指出arr是一个数组,而方括号为空则表明,可以将任何的数组传递给该函数。
  3. arr实际上并不是数组,而是指针,在编写函数其余部分时,可以将arr看做数组。

实例:
面程序用数组那样使用指针的情况。并使用sum_arr()函数计算总数,注意点sum_arr()函数使用arr时,就像是使用数组名一样。

#include <iostream>
using namespace std;
const int ArSize = 8;
int sum_arr(int arr[], int n);

int mian()
{
    int cookies[ArSize] = { 1,2,4,8,16,32,64,128 };

    int sum = sum_arr(cookies, ArSize);
    cout << "Total cookies eaten:" << sum << endl;
    return 0;
}
int sum_arr(int arr[], int n)
{
    int total = 0;
    for (int i = 0; i < n; i++)
        total = total + arr[i];
    return total;
}

在这里插入图片描述

  1. C++和C语言一样,也将数组名视为指针。c++将数组名计解释为其第一个元素的地址:cookies==&cookies[0];

  2. 首先,数组声明使用数组名来标记存储位置,次,对数组名使用sizeof将得到整个数组的长度;最后,将地址运算符&用于数组名时,将返回整个数组的地址。

//函数调用:

int sum=sum_arr(cookies,ArSize);
//cookies是数组名,而根据c++规则,cookies是其第一个元素的地址,因此函数传递的是地址。
//由于数组的元素的类型为int,因此cookies的类型必须是int指针,即 int*。
//正确的函数头应该如下面所示:
int sum_arr(int * arr,int n);
//其中用int *arr代替了int arr[]。都意味着arr是一个int指针。arr不仅指向int,还指向int数组的第一个int。

变量arr实际上就是一个指针。无论arr是指针还是数组名,表达式arr[3]指的是数组的第四个元素。下面的两个恒等式重要应该记住:

arr[i]==*(arr+1)
&arr[i]==arr+I;

将指针加1,实际上是加上了一个与指针指向的类型的长度的相等值。

6.显示数组及用const保护数组

创建显示数组步骤:将数组名和填充的元素数目传递给函数,然后该函数使用循环来表示每个元素。为防止函数无意修改数组的内容,可在声明形参时使用关键字const:

void show_array(const double ar[],int n)
{
	for(int i=0;i<n;i++)
	{
		cout<<"~~~"<<(i+1)<<": ";
		cout<<ar[i]<<endl;
	}
}
7.修改数组

对数组进行的第三项操作是每个元素与同一个重新评估因子相乘。需要传递三个参数:因子,数组和元素的数目。该函数不需要返回值:

void revalue(double r,double ar[],int n)
{
	for(int i=0;i<n;i++)
	ar[i] *=r;
}
//由于这个函数修改数组的值,因此在声明ar时,不能使用const。
8.数组处理函数的常用编写方式

编写一个double数组的函数,若函数修改数组,原型如下:

void arr_sum(double ar[],int n);

函数不修改数组,原型如下:

void arr_sum(const double ar[],int n);
9.函数和二维数组

编写二维数组作为参数的函数,数组名被视为其地址,因此,相应的形参是一个指针,就像一维数组一样,比较难处理的是如何正确使用指针 。例如:

int data[3][4]={{1,2,3,4},{9,8,7,6},{2,4,6,8}};
int total=sum(data,3);
//data是一个数组名,该数组有三个元素,第一个元素本身是一个数组,由4个int值组成。

因此data的类型是指向由4个int组成的数组的指针。因此正确的类型如下:

int sum(int (*ar2)[4],int size);

其中的括号是必不可少的,因为下面的声明将声明一个由4个指向int的指针组成的数组,而不是由一个指向由4个int组成的数组的指针。另外,函数参数不能是数组:

int *ar2[4]

还有另外一种格式,这种格式与上述原型的含义完全相同,但可读性更强:

int sum(int ar2[][4],int size);

由于参数ar2是指向数组的指针,那么我们如何在函数定义中使用它呢?最简单的方式是将ar2看做是一个二维数组的名称。下面是一个可行的定义:

int sum(int ar2[][4],int size)
{
	int total=0;
	for(int r=0;r<ssize;r++)
	for(int c=0;c<4;c++)
		total+=ar2[r][c];
		return total;
}

ar2[r]是编号为r的元素,由于该元素本身就是由4个int组成的数组,因此ar2[r]是由4个int组成的数组中的一个元素。是一个int的值。必须对指针ar2执行两次解除引用,才能得到数据。最简单方式是使用方括号两次:ar[r][c]

10.函数和结构
//定义结构:
struct travel_time
{
	int hours;
	int times;
	};
//返回两个这种结构的总和的sum()的原型
//返回值的类型应为travel_time,两个参数也应为这种类型
travel_time sum(travel_time t1,travel_time t2);

实例:

#include <iostream>
using namespace std;
struct travel_time
{
    int hours;
    int mins;
};
const int Mins_per_hr=60;

travel_time sum(travel_time t1,travel_time t2);
void show_time(travel_time t);

int main()
{
    travel_time day1={5,45};
    travel_time day2={4,55};

    travel_time trip =sum(day1,day2);
    cout<<"Two-day total:";
    show_time(trip);

    travel_time day3={4,332};
    cout<<"Three-day total:";
    show_time(sum(trip,day3));
    return 0;
}

travel_time sum(travel_time t1, travel_time t2)//构造函数
{
    travel_time total;
    total.mins=(t1.mins+t2.mins)%Mins_per_hr;
    total.hours=t1.hours+t2.hours+
            (t1.mins+t2.mins)/Mins_per_hr;
    return total;
}
void show_time(travel_time t)
{
    cout<<t.hours<<"hours,"<<t.mins<<"minutes"<<endl;
}

在这里插入图片描述
首先将分钟成员相加,然后通过整数除法得到小时值,通过求模运算符%得到剩余的分钟数。

实例:

下面的程序将定义两个结构来处理空间,用于表示两种不同的描述位置的方法,然后开发函数,将一种方式转换另一种格式。

#include <iostream>
#include<cmath>
using namespace std;
struct  polar//第一种直角坐标系
{
    double distance;
    double angle;
};

struct rect//第二种距离和方向
{
    double x;
    double y;
};
polar rect_to_polar(rect xypos);
void show_polar(polar dapos);

int main()
{
    rect rplace;
    polar pplace;
    cout << "Enter the x and y values:";
    while (cin >> rplace.x >> rplace.y)
    {
        pplace = rect_to_polar(rplace);
        show_polar(pplace);
        cout << "Next two numbers (q to quit):";
    }
    cout << "Done" << endl;
    return 0;
}
polar rect_to_polar(rect xypos)
{
    polar answer;
    answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y);//计算距离
    answer.angle = atan2(xypos.y, xypos.x);//计算角度
    return answer;
}
void show_polar(polar dapos)
{
    const double Rad_to_deg = 57.29577951;

    cout << "distance=" << dapos.distance;
    cout << ",angle=" << dapos.angle * Rad_to_deg;
    cout << "degrees" << endl;
}

在这里插入图片描述

11.传递结构

要传递结构的地址而不是整个结构以节省的时间和空间,使用结构指针。需要重新编写show_polar()函数,注意事项:

  1. 调用函数时,将结构的地址(&pplace)而不是结构本身(pplace)传递给它。
  2. 将形参声明为指向polar的指针,即polar*类型,由于函数不应该修改结构,因此使用const修饰符。
  3. 形参是指针而不是结构,应该使用间接成员运算符(->)而不是句点。

修改后:

void show_polar(const polar * pda)
{
    const double Rad_to_deg = 57.29577951;

    cout << "distance=" << pda->distance;
    cout << ",angle=" << pda->angle * Rad_to_deg;
    cout << "degrees" << endl;
}
12.函数和递归

调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。
若递归函数调用自己,则被调用的函数也将调用自己,这将无限循环下去,除非代码中包含终止调用的内容通常的方法将递归调用放在if语句中,如:void类型的递归函数recurs()的代码如下:

void recurs(argumentlist)
{
	statements1
		if(test)
			recurs(arguments)
				statement2
}
  1. test最终将为false,调用链将断开。
  2. 只要if语句为true,每个recurs()调用都将执行statements1,然后调用recurs(),而不会执行statement。
  3. 当if语句为false,当前调用将执行statement2。
  4. recurs()进行了5次递归调用,则第一个statement1部分将按函数调用的顺序执行5次,然后statement2部分将以与函数调用相反的顺序执行5次。
  5. 进入5层递归后,程序将返回。

实例1:

#include <iostream>
using namespace std;
void countdown(int n);
int main()
{
	countdown(4);
	return 0;
}
void countdown(int n)
{
	cout << "Counting down..." << n << endl;
	if (n > 0)
		countdown(n - 1);//不继续往下执行,将一直调用此函数
		
	cout << n << ":Kaboom!" << endl;
}1

在这里插入图片描述

实例2:使用递归计算第5个学生的年龄,第1为10岁,第5比第4大2岁,第4比第3大2岁,第3比第2大2岁,第2比第1大2岁。要得到第5个学生年龄必须知道第三,依次类推。

在这里插入图片描述

数学公式表达:
在这里插入图片描述


#include <stdio.h>
int age(int n);
int main()
{
    printf("ON5,age:%d\n",age(5));
    return 0;
}

int age(int n)
{
    int c;
    if(n==1)
        c=10;
    else
        c=age(n-1)+2;//递归四次,n=5是在main函数中调用
    return c;
}


/*步骤一:回溯
 * n=5在main函数调用
  *5:n=5; c5=age(5-1)+2;
  *
  *下面四次在age函数递归调用自己
  *4:n=4; c4=age(4-1)+2;
  *3:n=3; c3=age(3-1)+2;
  *2:n=2; c2=age(2-1)+2;
  *1:n=1; n=1不在递归; c1=age(1)=10;
  *
  *
  *步骤二:递归
  *n=1时,c1=age(1)=10;
  *n=2时,c2=age(1)+2=12;
  *n=3时;c3=age(2)+2=14;
  *n=4时;c4=age(3)+2=16;
  *n=5时;c5=age(4)+2=18;

 */

在这里插入图片描述

在这里插入图片描述

13.多个递归

实例:
下面程序使用递归函数subdivide(),该函数使用一个字符串,该字符串除两端为|字符外,其他为空格。mina()函数使用循环循环调用subdivide()函数6次。函数调用自身时,直至减到0,不在调用自己。

#include <iostream>
using namespace std;
const int Len = 66;
const int Divs = 6;
void subdivide(char ar[], int low, int high, int level);
int main()
{
	char ruler[Len];
	int i;
	for (i = 1; i < Len - 2; i++)
		ruler[i] = ' ';
	ruler[Len - 1] = '\0';
	int max = Len - 2;
	int min = 0;
	ruler[min] = ruler[max] = '|';
	cout << ruler << endl;
	for (i = 1; i <= Divs; i++)
	{
		subdivide(ruler, min, max, i);
		cout << ruler << endl;
		for (int j = 1; j < Len - 2; j++)
			ruler[j] = ' ';
	}
	return 0;
}
void subdivide(char ar[], int low, int high, int level)
{
	if (level == 0)
		return ;
	int mid = (high + low) / 2;
	ar[mid] = '|';
	subdivide(ar, low, mid, level - 1);
	subdivide(ar, mid, high, level - 1);
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值