C++(学习笔记)函数


前言

  本人在阅读C++ primer plus(第六版)的过程中做的笔记,写这篇文章既是为了分享,也是为了以后查阅。以下是本篇文章正式内容。


一、函数基本知识

  函数返回类型不能是数组,可以是其它任何类型(虽然不能直接返回数组,但可以将数组作为结构或对象的组成部分来返回)。
  为什么需要函数原型?原型描述了函数到编译器的接口,它将函数返回值的类型,参数类型和参数数量告诉编译器。

二、函数和数组

  1. 数组声明使用数组名来标记存储位置;对数组名使用sizeof将得到整个数组的长度(以字节为单位);将地址运算符&用于数组名将得到整个数组的地址。
  2. 非const变量的地址可以赋给非const指针;
    const变量的地址可以赋给const指针;
    非const变量的地址可以赋给const指针;
    const变量的地址不可以赋给非const指针;
    非const变量的地址赋给const指针时,如:
    int n = 9;
    const int *pn = &n;
    则不可以通过指针pn修改n的值,但是可以将新值直接赋给n,如:
    *pn = 10;  //不允许
    n = 10;  //允许
    也可以将新的地址赋给const指针,如:
    int m = 20;
    pn = &m;
    也可以使用const使指针的值无法修改,如:
    int * const pr = &n;
    这样,pr就只能指向n的地址而不能修改,但是可以通过pr来修改n的值。
    还可以声明指向const对象的const指针,如:
    const int * const pt = &n;

三、函数和二维数组

  函数的参数不能是数组,但可以是指针,幸运的是数组名即是数组的地址。在将二维数组作为函数的参数时,比较难处理的是如何在函数原型中正确地声明指针。假如有下面的二维数组:
  int data[3][4] = {{1, 2, 3, 4}, {9, 8, 7, 6}, {2, 4, 6, 8}};
  data是一个数组名,该数组有3个元素,第一个元素本身是一个由4个int组成的数组,所以data指向由4个int组成的数组。
  在将data作为函数的参数前,需要搞清楚下面两个声明语句的含义:
  int (*ar2)[4];    //声明1
  int *ar2[4];    //声明2
  在声明1中,ar2是一个指针,指向的是由4个int组成的数组;在声明2中,ar2是一个数组,由4个int指针组成。因此,将data作为函数参数时,正确的函数原型如下:
  int sum(int (*ar2)[4], int size);
  还有另一种格式,这种格式与上述原型的含义完全相同,但可读性更强:
  int sum(int ar2[][4], int size);
  指针类型指定了列数,因此sum()函数只能接受由4列组成的数组,但size参数指定了行数。由于参数ar2是指向数组的指针,那么如何在函数定义中使用它呢?最简单的方法是将ar2看作是一个二维数组的名称。下面是一个可行的函数定义:

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

  行数被传递给size参数,但无论是参数ar2的声明或是内部for循环中,列数都是固定的4列,因此sum()函数对数组的行数没有限制:
  int a[100][4];
  ……
  int total1 = sum(a, 100);    //数组a所有元素的和
  int total2 = sum(a, 10);    //数组a前10行元素的和
  int total3 = sum(a + 10, 20);  //数组a从第10行到第20行元素的和
  可以使用数组表示法的原因如下。由于ar2指向数组,因此表达式ar2+r指向编号为r的元素,即ar2[r]是编号为r的元素。该元素本身就是一个由4个int组成的数组,因此ar2[r]是该数组的名称。将下标用于数组名将得到一个数组元素,因此ar2[r][c]是由4个int组成的数组中的一个元素,是一个int值。必须对指针ar2执行两次解除引用才能得到数据,最简单的方法是使用两次方括号:ar2[r][c]。然而,如果不考虑难看的话也可以使用两次解除引用运算符*:ar2[r][c] == ((ar2 + r) + c);

四、函数和C风格字符串

1.C风格字符串为参数

unsigned int c_int_str(const char *str, char ch);			//函数原型
unsigned int c_int_str(const char *str, char ch)			//函数定义
{
		unsigned int count = 0;
		while (*str)
		{
			if (*str == ch)
			{
				count++;
				str++;
			}
		}
		return count;
}

  将字符串作为参数来传递,但实际传递的是字符串第一个字符的地址。这意味着字符串函数原型应将其表示字符串的形参声明为char*类型。

2.返回C风格字符串

char* buildstr(char ch, int n);							//函数原型
char* buildstr(char ch, int n)							//函数定义
{
		char* pstr = new char[n + 1];
		pstr[n] = ‘\0;
		while (n-- > 0)
		{
			pstr[n] = ch;
		}
		return pstr;
}

  在while循环中,执行到测试条件时,假如此时n = 1,则执行到括号结束时n = 0,因为后缀递减运算是先使用,后递减,即先判断1 > 0,执行循环体之前再将1减1。

五、递归

1.包含一个递归调用的递归

  例如,void类型的递归函数recurs()的代码如下:

void recurs(argumentlist)
{
		int n;
		statements1;
		if (testistrue)
			recurs(arguments);
		statements2;
}

  只要if语句为true,每个recurs()调用都将执行statements1,然后再调用recurs(),而不会执行statements2。当if语句为false,当前调用执行statements2。当前调用结束后,程序控制权返回给调用它的recurs(),而该recurs()将执行statements2,然后结束,并将程序控制权交给前一个调用,以此类推。因此,如果recurs()进行了5次递归调用,则statements1将按调用顺序执行5次,而statements2按与函数调用相反的顺序执行5次,5层递归后程序将沿进入的路径返回。每个递归调用都创建自己的一套变量,因此当程序到达5次调用后,将有5个独立的变量n。分析下面的代码:

#include<iostream>
using namespace std;

void recurs(int);

int main()
{
	recurs(4);
	return 0;
}

void recurs(int n)
{
	cout << "Counting down..." << n << endl;
	if (n > 0)
		recurs(n - 1);
	cout << n << ": Kaboom!\n";
}

运行结果如下图:
运行结果

运行结果

2.包含多个递归调用的递归

  分析下面代码:

#include<iostream>
using namespace std;

const int Len = 66;
const int Divs = 6;

void subdivide(char ch[], int left, int right, int level);

int main()
{
	char ruler[Len];
	ruler[Len - 1] = '\0';
	ruler[0] = ruler[Len - 2] = '|';
	for (int i = 1; i < Len - 2; i++)
		ruler[i] = ' ';
	cout << ruler << endl;
	for (int i = 1; i < Divs + 1; i++)
	{
		subdivide(ruler, 0, Len - 2, i);
		cout << ruler << endl;
		for (int j = 1; j < Len - 2; j++)
			ruler[j] = ' ';
	}
	return 0;
}

void subdivide(char ch[], int left, int right, int level)
{
	if (level == 0)
		return;
	int mid = (left + right) / 2;
	ch[mid] = '|';
	subdivide(ch, left, mid, level - 1);
	subdivide(ch, mid, right, level - 1);
}

运行结果如下图:
运行结果

运行结果

六、函数指针

1.函数指针的基础知识

  函数的地址是存储其机器语言代码的内存的开始地址。获取函数的地址很简单,只要使用函数名(后面不跟参数)即可,例如think()是一个函数,则think就是该函数的地址。声明函数指针必须指定函数的返回类型以及函数的特征标(参数列表),应该像声明函数原型那样声明函数指针。
  假如有函数double pam(int);
  则其函数指针声明为double (*pf)(int);
  pf就是其函数指针,可以将相应的函数地址赋给它,例如pf = pam;
  pam的特征标和返回类型必须和pf相同,否则编译器将拒绝这种赋值。
  使用方式:
  void estimate(int lines, double (*pf)(int));
  estimate(50, pam);
  详情见下面代码:

#include<iostream>
using namespace std;

double leon(int);
double leonbro(int);
void estimate(int, double(*pf)(int));

int main()
{
	int lns;
	cout << "输入需要计算的行数:";
	cin >> lns;
	cout << "leon计算出来的时间:";
	estimate(lns, leon);
	cout << "leonbro计算出来的时间:";
	estimate(lns, leonbro);
	return 0;
}

double leon(int lines)
{
	return 0.05 * lines;
}

double leonbro(int lines)
{
	return 0.03 * lines + 0.004 * lines * lines;
}

void estimate(int lines, double(*pf)(int))
{
	cout << (*pf)(lines) << " hours." << endl;
}

2.函数指针数组、指向函数指针数组的指针

  首先,有3个函数:
  const double * f1(const double ar[], int n);
  const double * f2(const double [], int);
  const double * f3(cosnt double*, int);
  这3个函数是完全相同的。
  假设指针pa指向这3个函数之一,只需将函数名替换为(*pa):
  const double
* (*pa)(const double *, int);
  可在声明时初始化:
  const double
* (*pa)(const double , int) = f1;
  也可以使用自动类型推断:auto pa = f2;
  声明一个函数指针数组包含这3个函数:
  const double
* (*pa[3])(const double *, int) = {f1, f2, f3};*pa[3]表明这是一个有3个元素的指针数组,其他部分指出了每个指针指向的类型是什么。
  另一件事是创建指向整个数组的指针,使用auto:auto pc = &pa;
  也可以自己声明:
  const double * ( *(*pd)[3])(const double *, int) = &pa;
  pd指向数组,*pd就是数组,而(*pd)[i]就是数组中的元素,即函数指针。
  分析下面的代码:

#include<iostream>
using namespace std;

const double* f1(const double ar[], int n);
const double* f2(const double*, int);
const double* f3(const double[], int);

int main()
{
	const double arr[3] = { 0.0, 1.1, 2.2 };
	const double* (*arr_fun_ptr[3])(const double*, int) = { f1, f2, f3 };//函数指针数组
	cout << fixed;
	cout.precision(1);
	for (int i = 0; i < 3; i++)
		cout << (*arr_fun_ptr[i])(arr, 3) << "   " << *(*arr_fun_ptr[i])(arr, 3) << endl;
	return 0;
}

const double* f1(const double ar[], int n)
{
	return ar;
}

const double* f2(const double* ar, int n)
{
	return ar + 1;
}

const double* f3(const double ar[], int n)
{
	return ar + 2;
}

3.使用typedef进行简化

  关键字typedef可以创建类型别名,因此也可以创建函数指针类型的别名:
  typedef const double* (*p_fun)(const double ar[], int); //p_fun现在是函数指针类型
  然后使用这个别名来简化代码:
  p_fun p1 = f1;    //p1指向函数f1()
  p_fun arr[3] = {f1, f2, f3};  //arr[3]是函数指针数组


总结

  以上就是本文的内容——记录了函数基本知识、函数和数组以及二维数组、函数中的C风格字符串、递归和函数指针。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值