函数的参数
形参:函数声明中表示的变量,比如下面的x
。
void foo(int x)
{
}
实参:函数调用时实际传递到函数的参数,比如下面的y
:
void foo(int x)
{
}
foo(y);
将参数传递给函数的主要方法有3种:按值传递,按引用传递和按地址传递。
通过值传递函数参数
通过值进行传递是最直观方式,示例:
#include <iostream>
void foo(int y)
{
std::cout << "y = " << y << '\n';
}
int main()
{
foo(5); // first call
int x = 6;
foo(x); // second call
foo(x+1); // third call
return 0;
}
将数字5
、x
、x+1
的值直接传递给函数,其特点就是将这些值赋值到函数的形参y
里,所以函数不会对原数据产生更改。
- When to use按值传递:除了结构和类,基本上可以传递所有。
- 优点:①不会修改参数;②传递类型很多
- 缺点:需要复制进函数,所以性能可能存在下降,在变量比较大或者运算次数较多时。
通过引用传递函数参数
为了解决变量传递函数参数的两个问题①复制会导致的性能变慢,②无法修改外部值只能通过返回值跟调用者交互;则通过引用传递函数参数可以解决这个问题。
语法:只需要函数声明时变成引用声明即可。
void addOne(int &ref) // ref is a reference variable
{
ref = ref + 1;
}
int main()
{
int value = 5;
cout << "value = " << value << '\n';
addOne(value);
cout << "value = " << value << '\n';
return 0;
}
由于引用可以修改外部参数,所以如果函数要返回多个值,除了结构体之外,还可以使用引用。但是这在语义上是不明显的,输出参数放到输入参数上,容易出错,所以并不推荐,如果要使用此功能,最好在需要更改的参数上加上前缀out-
。
const引用
如果只是想利用它不复制性能快的优点,但是不希望这个参数可以被改变,则可以给引用前加上const
限制。并且const
引用可以传递变量也可以传递常量,并且还能传递右值。
指针引用
可以通过引用传递指针,并使函数完全更改指针的地址。
#include <iostream>
void foo(int *&ptr) // pass pointer by reference
{
ptr = nullptr; // this changes the actual ptr argument passed in, not a copy
}
int main()
{
int x = 5;
int *ptr = &x;
std::cout << "ptr is: " << (ptr ? "non-null" : "null") << '\n'; // prints non-null
foo(ptr);
std::cout << "ptr is: " << (ptr ? "non-null" : "null") << '\n'; // prints null
return 0;
}
数组引用
引用数组时,如果涉及到for-each
循环、排序这种需要知道数组具体大小,则在引用时需要写明数组大小,并且&
符号用括号隔开如(&array)[4]
,要不然如果是&array[4]
则会变成对数组元素array[4]
的引用。
#include <iostream>
// Note: You need to specify the array size in the function declaration
void printElements(int (&arr)[4])
{
int length{ sizeof(arr) / sizeof(arr[0]) }; // we can now do this since the array won't decay
for (int i{ 0 }; i < length; ++i)
{
std::cout << arr[i] << std::endl;
}
}
int main()
{
int arr[]{ 99, 20, 14, 80 };
printElements(arr);
return 0;
}
通过指针传递函数参数
指针也是变量的一种类型,所以也可以用于传递函数参数。
通过地址传递的优点:
- 通过地址传递允许函数更改参数的值,这有时很有用。否则,可以使用const来确保函数不会更改参数。(但是,如果要使用非指针执行此操作,则应改用按引用传递)。
- 由于未复制参数,因此即使与大型结构或类一起使用时,它也很快速。
我们可以通过out参数从一个函数返回多个值。
通过地址传递的缺点:
- 因为文字和表达式没有地址,所以指针参数必须是普通变量。
必须检查所有值以查看它们是否为空。尝试取消引用空值将导致崩溃。很容易忘记这样做。 - 因为取消引用指针比直接访问值要慢,所以访问由地址传递的参数要比访问由值传递的参数慢。
何时使用按地址传递:
- 传递内置数组时(如果您可以接受它们会衰减为指针的事实)。
- 在传递指针和nullptr时,在逻辑上是有效的参数。
传递结构或类时(使用按引用传递)。
传递基本类型时(使用按值传递)。
如您所见,按地址传递和按引用传递具有几乎相同的优点和缺点。由于按引用传递通常比按地址传递更安全,因此在大多数情况下应首选按引用传递。
普通的按指针传递函数的示例:
#include <iostream>
void foo(int *ptr)
{
*ptr = 6;
}
int main()
{
int value = 5;
std::cout << "value = " << value << '\n';
foo(&value);
std::cout << "value = " << value << '\n';
return 0;
}
关于指针传递的几个注意事项:
①由于传递数组时数组会衰减成指针,所以长度参数必须单独传递。
//输出数组的每一个数字
void printArray(int *array, int length)
{
if (!array)
return;
for (int index=0; index < length; ++index)
cout << array[index] << ' ';
}
int main()
{
int array[6] = { 6, 5, 4, 3, 2, 1 };
printArray(array, 6);
}
②如果传入的指针参数不可修改,则使用const
关键字,比如上面的子函数定义可以改为:
void printArray(const int *array,int length)
③指针也是一种变量,所以把指针当成变量的话很容易理解,传入函数的实参实际上是复制进形参的,也就是形参这个变量在函数内部可以被改变指向其他的地址,这并不会对外部的指针及其它指向的变量产生影响。
如果对形参解引用才会对该位置的变量产生影响。
#include <iostream>
void setToNull(int *tempPtr)
{
tempPtr = nullptr;
}
void setToNull_2(int *tempPtr)
{
*tempPtr = 6;
}
int main()
{
// First we set ptr to the address of five, which means *ptr = 5
int five = 5;
int *ptr = &five;
// This will print 5
std::cout << *ptr;
// tempPtr is set as a reference to ptr
setToNull(ptr);
if (ptr)
std::cout << *ptr;
else
std::cout << " ptr is null";
setToNull_2(ptr);
if (ptr)
std::cout << *ptr;
else
std::cout << " ptr is null";
return 0;
}
输出:
55
6
④如果就是想通过函数修改实参指针的指向呢?正好,这就是引用的功能,使用引用指针即可。
将上述子函数改成:
void setToNull(int *&tempPtr)
{
tempPtr = nullptr; // use 0 instead if not C++11
}
以值、引用和指针方式函数return
留
白
以
后
补
充