指针与函数
知识点
如何在函数中修改传入变量的值?一种常用的方法是使用指针。
void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } int main() { int a = 1, b = 2; swap(&a, &b); }
求解一元二次方程的函数
知识点
//题目描述: //请实现函数 `int SolveQuadratic(double a, double b, double c, double *px1, double *px2)`,返回一元二次方程 ax^2 + bx + c = 0 的两个根,并返回不同的根的个数。如果 a=0,返回3。 //输入:无 //输出: //2 //2 1 #include <iostream> int SolveQuadratic(double a, double b, double c, double *px1, double *px2) { double disc, sqrtDisc; if(a == 0) return 3; disc = b * b - 4 * a * c; if( disc < 0 ) return 0; if ( disc == 0 ) { *px1 = -b /(2 * a); return 1;} sqrtDisc = sqrt(disc); *px1 = (-b + sqrtDisc) / (2 * a); *px2 = (-b - sqrtDisc) / (2 * a); return 2; } int main() { double x1, x2; std::cout << SolveQuadratic(1, -3, 2, &x1, &x2) << std::endl; std::cout << x1 << " " << x2 << std::endl; }
返回指针的函数
知识点
指向全局变量的指针可以作为函数返回值。
但局部变量不可以。局部变量的生命周期为:声明时,直到当前作用域(花括号)结束。函数返回后,该局部变量对应内存被回收,指向它的指针无意义。
指向new得到的内存的指针可以作为返回值。该内存在堆上分配,直到被delete之前都不会被回收。
int tmp; int *func1 (int val) { int a; int *b = new int [2]; // return &val; // err: val的生命周期在return时就结束了 // return &a; // err: 同上 // return b; // ok return &tmp; // ok }
引用概念
知识点
引用可以理解为变量的"别名"。同时,也可以理解为一个type *const指针,即指针指向的对象的值可变,但指针本身的地址不可变。在指针的基础上,引用省略了取地址和解引用。
对引用的操作(求值,修改等)始终绑定在原对象上。
int a = 1; int &b = a; // a == 1; b == 1 a = 2; // a == 2; b == 2 b = 3; // a == 3; b == 3 cout << sizeof(b) << endl; // 4; 和sizeof(a)相同 cout << &b << ' ' << &a << ' ' << (&a == &b) << endl; // true; a和b的地址是一样的 int *c = &b; // 此时c指向a cout << sizeof(c) << endl; // 64位系统上为8; 和上面sizeof(b)做对比
引用传递
知识点
如果想在函数内修改一个对象(而非数组)的值,传引用是更现代的方式。
引用的另一个作用是可以让函数返回多个值。此时,只要传入多个引用,然后把他们当作返回值修改即可。
void swap(int &a, int &b) { int tmp = a; a = b; b = tmp; } void return2value(int &a, int &b) { //returns {3, 4} a = 3, b = 4; } int main() { int a, b; return2value(a, b); swap(a, b); cout << a << ' ' << b << '\n'; }
即使不需要修改传入对象的值,按引用传递往往也是更高效的方式。此时,常常使用常量引用传递。
这对于大对象的传递尤其有效。按值传递大对象时,会导致对整个对象的拷贝,可能很慢;而按引用传递时,开销仅仅为传一个指针(64 位计算机中为 8 bytes)。
因此,大多数时候,C++的函数参数为常量引用或普通引用,非引用/指针的参数出现较少。
// struct的知识会在后面学到,现在只需要知道BigType是个很大的对象: sizeof(BigType) == 4000 struct BigType { int val[1000]; }; void func1(BigType a) // copies 4000 bytes { // ... } void func2(const BigType &a) // copies 8 bytes { // ... } //题目描述: //实现函数`myFindCnt`,返回`vector`数组中某个值`val`第一次出现的下标。 //如果该值不存在,返回数组的长度。 //同时,要在函数的cnt参数中返回这个值出现的次数。 //输入:无 //输出: //4 //2 #include <iostream> #include <vector> using namespace std; int myFindCnt(const vector<int> &arr, int val, int &cnt) { int res = arr.size(); cnt = 0; for (int i = 0; i < arr.size(); ++i) { if (arr[i] == val) { if (res == arr.size()) { res = i; } ++cnt; } } return res; } int main() { vector<int> arr = {1, 2, 6, 1, 3, 4, 2, 4, 3}; int cnt; std::cout << myFindCnt(arr, 3, cnt) << std::endl; std::cout << cnt << std::endl; }
知识点
函数也可以返回变量的引用,此时和指针类似。可以返回的引用包括:
对全局变量的引用
返回函数的引用类型参数
返回函数指针类型参数解引用之后的结果
同上,不能返回局部变量的引用。
int glob; int& func(int a, int &b, int *c) { int tmp; return a; // err return tmp; // err return b; // ok return *c; // ok: *c为引用类型 return glob; // ok }