函数——c++的编程模块

函数的基本知识

C++对于函数的返回类型有一定的限制:不能是数组,但可以是其它的任意数据类型(整数,浮点,指针,甚至结构和对象,结构中包含数组也可以)
在这里插入图片描述

函数参数和按值传递

注意:(109)/(21) = 90/2 = 45
(10/2)*(9/1) = 45;这两种方法得到的结果相同,但前者的中间值90大于后者。因子越多,中间值的差值就越大。当数字非常大时,这种交替进行乘除运算的策略可以防止中间的结果超出最大浮点数。

函数和数组

C++将数组名解释为第一个元素的首地址。对数组名使用sizeof得到的是整个数组的长度(字节为单位)。当且仅当用于函数头或者函数原型中,int a 和int a[] 是等价的。
a[i] = =
(a+i)
&a[i] = a+i;

using namespace std;
int sum(int* a, int n)
{
	cout << a << " address " << endl;
	cout << "sizeof : " << sizeof(a) << endl;  // 此处每次是4
	int total = 0;
	for (int i = 0; i < n; i++)
	{
		total += a[i];
	}
	return total;
}
int main()
{
	int a[5] = { 1,2,3,4,5 };
	cout << a << " address " << endl;
	cout << "sizeof : " << sizeof(a) <<endl;   //20

	int count = sum(a, 2);
	cout << "count :" <<count<< endl;  // 3

	count = sum(a+2, 2);   //sum(&a[2],2) 一样
	cout << "count :" << count << endl;  // 7

}

指针和const

  1. 指针指向一个常量对象
    int age = 20;
    const int * pa = &age;
    不能用pa修改age的值。pa的值为const,不能被修改。pa的声明并不意味着他指向的值实际上是一个常量,而是意味着对pa而言这个值是常量。pa指向age,age不是const。因此pa = 20; //error age = 20; //ok
  2. 指针本身声明为常量
    const int age = 20;
    const int * pa = &age; //ok
    const int age = 20;
    int * pa = &age; //error 可以使用pa修改age。C++禁止这种,如果非要这样,使用强制转换。
    注意:1.仅当有一层间接关系(如指针指向基本的数据类型),才可以将非const的地址或指针,赋给const指针。
    1. const int a[2] = {0};
      int sum(int* a, int n);
      sum(a,2);//禁止

尽量使用const

  1. 可以避免由于无意间修改数据而导致的编程错误
  2. 使用const 使得函数能够处理const 和非const 实参,否则将只能接受非const数据。
  3. int age = 20;
    const int * pt = &age; const 只能防止修改pt指向的值(20),而不能修改pt的值。即可以将新地址赋给pt。
    int sag = 0;
    pt = &sag;但仍然不能使用pt来修改指针的值。
  4. int slo = 3;
    const int ps = &slo; //指针指向const int 不允许使用 ps修改slo的值,允许ps指向另一个位置。
    int const finger =&slo; //const 指针指向int ,只能指向slo,但允许使用finger修改slo的值
    即:finger 和
    ps 都是const
    const 放在
    左边表示指向的值不可以通过指针修改,*右边表示指针指向的值不能修改。

函数和二维数组

int *a[4] ; 指针数组。 由4个指向int的指针组成的数组
int (*a) [4] ; 数组指针 一个指向由4个int组成的数组的指针。 (数组指针,指向一个数组,该数组由4个int 组成)
int a[3][4] ; a是一个数组名,该数组有三个元素,第一个元素本身又是一个数组,由4个int组成。
因此 函数声明:int sum(int (*a) [4] ,int size)或者int sum(int a【】 [4] ,int size);
上述两个声明都指出a是指针而不是数组。指针类型指出由4个int组成的数组,因此指定了列数。没有指定函数。
int a[100][4];
int b[6][4];
sum(a,100);
sum(a,10);

//指针类型指定了列数,只能接受4列的二维数组,但对行数没有限制
//没有使用const 因为这种技术只能用于指向基本类型的指针,而a是指向指针的指针。
int sum(int (*a)[4],int row)
{
	int total = 0;
	for(int i=0;i<row;i++)
	{
		for(int j=0;j<4;j++)
		{
			total += a[i][j];   //
		}
	}
	return total;
}
//a 指向数组的第一个元素(它的元素由4个int组成),因此a+2 表示编号为2的元素。又该元素是一个由4个int组成的数组,因此a【2】是由4个int元素组成的数组名称。a【2】【1】 一个元素的值。或者  *(*(a+2)+1) 
//a  指针 指向第一行的由4个int 组成的数组
//a +r   指针指向第r行,该行由4个int组成
//*(a +r) 第r行的指针				 a【r】 
//   *(a +r)+c   第r行第c列的指针    a【r】 +c
//  *(*(a +r)+c)   第r行第c列的值   a【r】【c】

函数和C风格字符串

C风格字符串由一系列字符组成,以空值字符结尾。将字符串作为参数时意味着传递的是地址,但可以使用const禁止对字符串参数进行修改。

将C风格的字符串作为参数的函数

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

  1. char 数组
  2. 用引号括起的字符串常量(也称字符串字面值)
  3. 被设置为字符串的地址的char指针
    char ghost[15] = “helloworld”;
    char *str = “helloword”;
    int n1 = strlen(str); //指针 char
    int n2 = strlen(ghost); //ghost is &ghost【0】
    int n2 = strlen(“hello”); string的地址
    将字符串作为参数传递,实际是传递的字符串第一个字符的地址,由于有\0结尾,因此不必传递长度给函数。
    char 数组与常规C风格字符串的区别,字符串有内置的结尾符,若数组没有,则只是数组,不是字符串。
unsigned int c_in_str(const char* str, char ch)
{
	unsigned int count = 0;
	while (*str)
	{
		if (*str == ch)
		{
			count++;
		}
		str++;
	}
	return count;
}
int main()
{
	char mm[15] = "minmumn";
	const char* wail = "ululuuu";
	unsigned int m = c_in_str(mm,'m');
	unsigned int u = c_in_str(wail, 'u');
	cout << m << endl;
	cout << u << endl;
}

返回C风格字符串的函数

函数无法返回一个字符串,但能返回一个字符串的首地址

char* bulid(char ch, int n)
{
	char* pstr = new char[n + 1];    //作用域在函数内,函数结束时pstr使用的内存将被释放,但由于
	pstr[n] = '\0';                 //函数返回了pstr,因此可以在main函数中的指针ps来访问该段内存。
	while (n-->0)   //从后向前是为了使用额外的变量
	{
		pstr[n] = ch;
	}
	return pstr;
}
int main()
{
	char* ps = bulid('w', 7);
	cout << ps << endl;
	delete [] ps;
}

函数和结构

缺点:如果结构非常大,则复制结构将增加内存要求,降低系统运行的速度。可以使用地址或者引用。

struct polar {
	double distance;
	double angle;
};
struct rect {
	double x;
	double y;
};
polar rect_to_polar(rect re)
{
	polar p;
	p.distance = sqrt(re.x * re.x + re.y * re.y);
	p.angle = atan2(re.y ,re.x);
	return p;
}
void  show_polar(polar p)
{
	const double Rad_to_deg = 57.29;
	cout << "distance  :" << p.distance;
	cout << "degree : " << p.angle * Rad_to_deg;
}
int main()
{
	rect r{ 5, 5 };
	polar p = rect_to_polar(r);
	show_polar(p);
}
  1. while(cin>>r.x>>r.y)
  2. for(int i=0;i<n;i++)
    {
    cin>>tmp;
    if(tmp<0) break; //提早结束循环输入一个负值
    }
    cin消除了限制,接受任何有效的数字输入。此外,非数字输入将设置一个错误条件,禁止进一步读取输入。(使用clear取消禁止)

传参结构体地址

void rect_to_polar(const rect *re,polar *p)
{
p->distance = sqrt(re->x * re->x + re->y * re->y);
p->angle = atan2(re->y ,re->x);
}

函数和string对象

函数和array对象

递归

自己调用自己
调用一次导致两个调用,导致4个,导致8个,6层调用填充64个元素的原因 2^6 = 64

void SubDivide(char a[], int low, int high, int level)
{
	if (level == 0)
	{
		return;
	}
	int mid = (low + high) / 2;
	a[mid] = '|';
	SubDivide(a, low, mid, level - 1);
	SubDivide(a, mid, high, level - 1);
}
int main()
{
	const int len = 66;
	const int Divs = 6;
	char ruler[len];
	for (int 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 (int i = 1; i < Divs; i++)
	{
		SubDivide(ruler, min, max, i);
		cout << ruler << endl;
		for (int j = 1; j < len - 2; j++)
		{
			ruler[j] = ' ';
		}
	}
}

输出:
在这里插入图片描述

函数指针

函数的地址是存储其机器语言代码的内存的开始地址。可以编写将另一个函数的地址作为参数的函数,这样第一个函数就能找到第二个函数,并运行它。与直接调用另一个函数相比,这种方法很笨拙,但它允许在不同的时间传递不同函数的地址,意味着可以在不同的时间使用不同的函数。
一个函数中调用其他功能相同,实现不同的函数

  1. 获取函数地址。
    若f()为函数,则将函数作为参数传递的方法: fb(f) //直接为函数名称,不加(),加了则默认的是函数的返回值为参数
  2. 声明函数指针
    声明指向某种数据类型的指针时,必须制定指针指向的类型。同样,声明指向函数的指针时,也必须指定指针指向的函数类型。
    若函数原型如: double f(int a); 则函数指针double (*pf) (int); (*pf) 代替了f , (*pf) 就是函数,pf就是函数指针
    定义了函数指针之后,将函数地址赋给指针。 pf =f; 特征标必须相同
  3. 使用指针调用函数
    double x = f(3); 或者 x= (*pf)(3); C++也允许x= pf(3);

深入探讨函数指针

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

三个函数声明完全相同,若要定义则必须加标识符
声明一个函数指针,并在声明的时候初始化
const double* (p1)(const int, int) = f1;
可以使用C++11的自动类型推断功能
auo p2 = f1;
//
cout<< (p1)(av,3)<<" : "<<(*p1)(av,3)<<endl;
cout<< p2(av,3)<<" : "<<p2(av,3)<<endl;
两者都调用指向的函数f1和f2,并将av和3,作为参数。返回const double
,因此,前面打印返回double值得地址,后面显示地址的值。

  1. 含有三个函数指针的数组
    const double* (pa【3】)(const int , int ) = {f1,f2,f3};
    一方面pa是一个包含三个元素的数组,因此pa【3】,该声明的其它部分已经说明数组包含的元素是什么样子了。【】的优先级高于*,因此*pa【3】表示pa是一个包含3个指针的数组,其中每个指针都指向这样的函数,返回…,参数…。
    注意:此处不能使用auto,auto只能用于单值初始化,而不能用于初始化列表。但声明pa后,可以auto pb = pa;
  2. 调用 const double * a = pa0; 或者 const double * a = (pb【1】)(av,3);
    获得double的值可以
    pa0; *(*pb【1】)(av,3)
  3. 创建一个指向数组的指针
    auto pc = &pa // c++11
    const double* (*(pd)【3】)(const int , int ) = &pa;
    调用:pd指向数组,*pd就是数组,(pd)【i】是 数组中的元素,即函数指针。因此简单的调用.(pd)i; 指向的值(pd)i
    也可以(
    pd)【i】)(av,3)调用函数。获得返回的值:
    (*pd)【i】)(av,3)。
    注意:pa (是数组名,表示地址)和&pa之间的差别。大多数情况下,pa是第一个元素的地址,即&pa【0】,它是单个指针的地址。&pa是整个数组的地址(3个指针块)。从数字上说,两者的地址值相同,但+1后,前者为数组中元素的下一个地址,后者为下一个12字节的聂村块地址。另一个差别是,要得到第一个元素的值,只需要对pa解除一次引用。但对&pa需要解除两次引用。
    **&pa == *pa == pa[0];
const double* f1(const double* ar, int) { return ar; }
const double* f2(const double ar[], int) { return ar+1; }
const double* f3(const double ar[], int n) { return ar+2; }

int main()
{
	double av[3] = { 5.6, 8.9, 9.9 };
	const double* (*p1)(const double*, int) = f1;
	auto p2 = f2;
	cout << "using pointer to functions \n";
	cout << "adress value  " << (*p1)(av, 3) << " : " << *(*p1)(av, 3) << endl;
	cout << "adress value  " << p2(av, 3) << " : " << *p2(av, 3) << endl;
	//函数指针数组
	const double* (*pa[3])(const double*, int) = { f1,f2,f3 };
	auto pb = pa;
	cout << "函数指针数组\n";
	for (int i = 0; i < 3; i++)
	{
		cout << pa[i](av, 3) << " : " << *pa[i](av, 3) << endl;
		cout << pa[i](av, 3) << " : " << *pa[i](av, 3) << endl;
	}

	auto pc = &pa;
	cout << (*pc)[2](av, 3) << " : " << *(*pc)[2](av, 3) << endl;

	const double* (*(*pd)[3])(const double*, int) = &pa;
	const double* pdb = (*pd)[1](av, 3);
	cout << pdb << " : " << *pdb << endl;
	
	cout << (*(*pd)[2])(av, 3) << " : " << *(*(*pd)[2])(av, 3) << endl;
}

使用typedef进行简化

typedef const double* (p_fun)(const double, int) ;
p_fun p1 = f1;

p_fun pa[3] = {f1,f2,f3};
p_fun (*pd)[3] = &pa;
使用typedef可减少输入量,让编写代码时不易犯错,并让程序更容易理解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值