C++ 函数的参数传递

你离开我真会死。 提交于 2020-01-30 00:38:54

形参与实参

形参是在定义函数名和函数体的时候使用的参数,目的是用来接收调函数时传递的参数。

实参是在调用时传递给函数的参数,即传递给被调用函数的值。实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。

形参初始化

  • 形参初始化的机理与变量初始化一样。

  • 形参的类型决定了形参和实参交互的方式。
    如果形参是 引用 类型,它将绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。

  • 形参若是引用类型,也就是 引用形参 是 它对应的实参的别名。
    否则,在实参的值拷贝给形参后,形参 和 实参分别是两个独立的对象。

  • 而对于指针形参,与拷贝赋值同理,但是可以通过形参指针改变对象的值。(对象不是 const 或者指针不是 const int*)

传引用参数

即将 实参的对象绑定个形参,和 普通引用没有区别。

但是 传递引用参数 可以避免拷贝。 拷贝大的类类型或者容器对象是比较低效的,或者有些根本不支持拷贝。

使用 引用参数 返回额外的信息。
可知,一个函数只能返回一个值,但是利用 引用形参,可以达到 等效的 返回多个值的结果。

如:求一个字符串中,a 和 b 出现的次数。便可以这样:

	int cal(const string &str,int &cnta) {
		int cntb = 0;
		for(auto it : str) {
			if(it == 'a') ++ cnta;
			else if(it == 'b') ++ cntb;
		}
		return cntb;
	}

const 形参和实参

对于这类,需要注意顶层 const。 顶层 const 作用于对象本身。

用实参初始化形参时会忽略掉 顶层const,即形参的顶层 const 被忽略掉了。

当形参有顶层 const 时,传给他常量对象或者非常量对象都可以。

即:

	void fcn(const int i) { /* fcn 能够读取 i,但是不能改变 i */ }

调用 fcn 函数时,可以传入 const int , 也可以传入 int

!!注意:

	void fcn(const int i) { }
	void fcn(int i) { }	

不构成函数重载。因为上边的 const 被忽略掉了。

指针或引用形参与 const
	int i = 0;
	const int ci = i;
	string::size_type ctr = 0;
	reset(&i);				// 调用的 reset (int *i) 形式
	reset(&ci);			// 不正确,不能用 const int 对象的指针初始化 int *
	reset(i);				// 调用的 reset(int &i) 形式
	reset(ci);				// 不正确,不能把普通引用绑定到 const 对象 ci 上
	reset(42);				// 不正确,不能把普通引用绑定到字面值上,(const 引用可以)
	reset(ctr);				// 错误,类型不对
尽量使用常量引用

把函数不会改变的形参定义成普通引用是一种比较常见的错误,这么做带给函数的调用者一种误导,即函数能够修改它的实参的值。此外,使用引用而非常量引用极大地限制函数能够接受的实参类型。如上,不能把 const 对象、字面值 或者需要类型转换的对象传给普通的引用形参。

有函数:

	string::size_type find_char(const string &s,char c,string::size_type &occurs);	

若定义成:

	string_size_type find_char(string &s,char c,string::size_type &occurs);	

那么在调用 find_char(“hello world”, ‘o’, ctr) 时,将发生编译错误、。、、

还有一个问题:
假如有正确的函数 is_sentence(const string &s),但是 string_size_type find_char(string &s,char c,string::size_type &occurs);
若函数 is_sentence 是这样的:

		bool is_sentence(const string &s) {
			string::size_type ctr = 0;
			return find_char(s, '.', ctr) == s.size() - 1 && ctr == 1;
		}	

那么也会编译出错。
解决方法是: 将 is_sentence 也定义为 string &,但这样只是转移错误。 正确的: 在 is_sentence 中定义一个 string,作为 s 的副本,再传给 find_char,或者修改 find_char 为 const string &

那么 const string& 的好处:可以处理传入实参直接使用 字面值的情况,可以传入C风格字符串,减小局限性,同时可以避免长字符串的拷贝,提示效率。

测试代码:

#include <bits/stdc++.h>
using namespace std;
bool Is_empty(const string &s) {
    return s.empty();
}
int main() {
    char ch[] = "hhh";
    cout << Is_empty("") << endl;
   	cout << ch << endl;
    return 0;
}

对比代码:

#include <bits/stdc++.h>
using namespace std;
bool Is_empty(string &s) {
    return s.empty();
}
int main() {
    char ch[] = "hhh";
    cout << Is_empty("") << endl;   //!!!!报错
    cout << Is_empty(ch) << endl;   //!!!!报错
    return 0;
}

数组形参 和 const

首先需要知道数组的两个特性: 数组不允许拷贝,使用数组时(通常)将其转化为指针。

所以呢,当函数传递一个数组时,实际上传的是指向数组首元素的指针。

尽管不能以传递值的方式传递数组,但是可以把形参写成类似数组的形式:

	void print(const int *);
	void print(const int[]);
	void print(const int[10]);	// 这里的维度表示我们期望数组含有多少个元素,实际上不一定.

以上三个函数方式是等价的,每个函数的形参都是 const int *,调用时只检查传入的参数是否是 const int *

	int i = 0,j[2] = {0 , 1};
	print(&i);			// 正确, &i 是 int * 类型
	print(j);			// 正确,j 转换成 int * 并指向 j[10]

如果我们传给 print 函数的是一个数组,则实参自动转换成指向数组首元素的指针,数组的大小对函数的调用没有影响

!!!注意以数组作为形参的函数也必须确保使用数组时不会越界。

由于数组是以指针的形参传递给函数的,所以并不知道数组的确切尺寸。对于这个,有三种常用的技术:

  • 数组本身自带结束标记,如C风格字符串,自带 ‘\0’,
   void print(const char *p) {
   	if(p) {
   		while(*p)
   			cout << *p ++;	
   	}
   }

这种适用于结束标记不会与普通数据混淆的情况。

  • 使用标准库规范,利用数组首元素和尾后元素指针
	void print(const int *beg,const int *end) {
		while(beg != end) {
			cout << *beg ++ << endl;
		}
	}

调用,如:

	int j[2] = {0 , 1};
	print(begin(j),end(j));
  • 显示传递数组的大小
	void print(const int *ia, size_t size) {
		for(size_t i = 0;i < size;++ i) {
			cout << ia[i] << endl;
		}
	}
	///调用
	int j[] = {0, 1};
	print(j,end(j) - begin(j));

只要传递给函数的size值不超过实际大小,就是安全的。

数组形参和 const

三个 print 函数都把数组定义成了指向 const 的指针,关于引用的讨论同样适用指针。当函数不需要对数组元素进行写操作时,数组形参应该是指向 const 的指针。只有当函数确实要改变数组元素值时,才把形参定义成指向非常量的指针。

数组引用形参

c++允许将变量定义成数组的引用。同理,形参也可以是数组的引用,此时引用绑定到对应的实参上。

	void print(int (&arr)[10]) {
		for(auto it : arr) {
			cout << it << endl;
		}
	}

但是和前面的有所不同,此函数只能用于大小为10的数组

	int i = 0,j[2] = {0,1};
	int k[10] = {0,1,2,3,4,5,6,7,8,9};
	print(&i);			//错误
	print(j);			//错误
	print(k);			//正确
传递多维数组

多维数组其实是数组的数组,所以首元素是一个指向数组的指针。即

	void print(int (*matrix)[10],int rowsize) { /* */ }   ///括号是不能省略的,有括号和没括号意义不同
	//等价定义
	void print(int matrix[][10],int rowsize) { /* */ }

需要注意,数组第二维以及后面的更高维的大小都是不能省略的。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!