函数和二维数组
1 2 3 4 5 6 7 8 9 10
| int sum(int (*arr)[2], int n) { int res = 0; for(int i = 0; i < n; ++i) for(int j = 0; j < 2; ++j) res += arr[i][j]; return res; }
int a[3][2] = { {1, 2}, {3, 4}, {5, 6} }; cout << sum(a, 3) << endl;
|
使用二维数组作为参数,必须指定第二维的维数,用来表示元素的类型。
如上代码所示,表示arr
为一个数组名,而数组的每一个元素也是一个数组,由2个int
组成,即arr
的类型是指向元素类型为2个int
的数组的指针。
其中的括号必不可少,因为int *arr[2]
表示由2个int
型指针组成的数组,但函数参数不能为数组。
除此之外,也可以写为int sum(int arr[][2], int n);
,二者含义相同。
只要记住arr
为指针而非数组,即函数的参数不能为数组即可。
函数和字符串
字符串作参数
1 2 3 4 5 6 7 8 9 10
| void show_str(const char* str) { while (*str) { cout << *str; ++str; } cout << endl; }
char str[] = "hello world"; show_str(str);
|
可以将字符串作为参数,使用char*
(指向char
类型的指针)作为类型。
1
| void show_str(const char* str);
|
也可使用如下格式,意义相同。
1
| void show_str(const char str[]);
|
字符串作返回值
返回字符串的地址即可。即返回指向字符串首地址的指针。 注意不能返回栈空间的内存,因为函数执行完后便释放了,所以需要使用new
关键字申请堆空间的内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> using namespace std;
char* build_str(char ch, int n) { char* p_str = new char[1+n]; p_str[n] = '\0'; while(n-- > 0) p_str[n] = ch; return p_str; }
int main() {
char* str = build_str('d', 10); cout << str << endl; delete [] str;
}
|
函数和结构体
在函数里,我们只要像对待基本数据类型一样对待结构体即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> using namespace std;
struct pos { int x, y; };
pos sum_pos(pos a, pos b) { pos res; res.x = a.x + b.x; res.y = a.y + b.y; return res; }
int main() { pos m{1, 2}; pos n = {3, 4};
pos r = sum_pos(m, n); cout << r.x << " " << r.y << endl;
}
|
我们可以将结构体的地址作为参数或者返回值,以节省时间和空间,但是要注意不能返回栈空间的局部变量的临时内存。
1 2 3 4 5 6 7 8 9 10 11
| pos* sum_pos(const pos* a, const pos* b) { pos* res = new pos; res->x = a->x + b->x; res->y = a->y + b->y; return res; }
pos* r = sum_pos(&m, &n); cout << r->x << " " << r->y << endl; delete r;
|
也可以将结果作为指针直接放在参数中,而不用返回值。
1 2 3 4 5 6 7
| void sum_pos(const pos* a, const pos* b, pos* res) { res->x = a->x + b->x; res->y = a->y + b->y; } pos r; sum_pos(&m, &n, &r); cout << r.x << " " << r.y << endl;
|
string
、array
、vector
等类对象,都可看作基本类型,像结构体一样在函数中使用。
递归函数
1 2 3 4 5
| int factorial(int n) { if(n == 1) return 1; return n * factorial(n-1); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void quick_sort(int a[], int L, int R) { if(L >= R) return; int x = L, y = R, p = a[L]; while(x < y) { while(x < y && a[y] >= p) --y; a[x] = a[y]; while(x < y && a[x] <= p) ++x; a[y] = a[x]; } a[x] = p; quick_sort(a, L, x-1); quick_sort(a, x+1, R); }
int a[] { 5, 3, 2 ,1, 4}; quick_sort(a, 0, 4); for(int i = 0; i < 5; ++i) cout << a[i] << endl;
|
除了main
函数外,函数都可以调用自身,这种在实现中调用自身的函数称为递归函数。
一个递归函数必须有递归调用的终点,否则将会无限调用下去。
每一个递归过程中的函数都是独立的,递归的过程与普通函数调用的过程是一样的。我们只需将函数里对自己本身的调用也看作是对其他函数的调用,只不过功能相同而已,就可以很清楚地理解递归函数了。
函数指针
在c++
中,函数名即为函数的地址(不带括号)。 如有一个函数bool cmp(int a, int b);
,则cmp
即为该函数在内存中的地址。
声明
1 2
| bool cmp(int a, int b); bool (*p_cmp)(int a, int b);
|
如上代码所示,由于(*p_cmp)
与cmp
等价,所以(*p_cmp)
也是函数,则p_cmp
就是指向该函数指针了。
如果不加括号的话,bool *p_cmp(int a, int b);
表示一个返回bool*
的函数,而非函数指针。所以声明函数指针一定要加括号。
赋值
我们可以将具体的某个函数赋值给函数指针,前提是该函数的参数和返回类型都与函数指针的一致。
调用
我们可以使用函数指针来调用其指向的函数。可以使用(*pf)
调用,也可以直接使用指针名作为函数名调用。
下面两句的含义相同。
1 2
| cout << (*p_cmp)(1, 2) << endl; cout << p_cmp(1, 2) << endl;
|
下面代码为使用函数指针的一个绝佳例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <iostream> using namespace std;
bool cmp(int a, int b) { return a > b; } bool cmp_2(int a, int b) { return a <= b; }
int max_i(int a, int b, bool (*p_cmp)(int a, int b)) { if(p_cmp(a, b)) return a; else return b; }
int main() { cout << max_i(1, 2, cmp) << endl; cout << max_i(1, 2, cmp_2) << endl; }
|
其输出结果如下。
以 函数指针为元素 的数组
1
| int (*p_s[3])(int, int);
|
[]
的优先级高于*
,故(*p_s[3])
表示一个包含三个指针的数组。
指向 函数指针数组 的指针
我们将上面的p_s
替换成(*p_ps)
。
1
| int (*(*p_ps)[3])(int, int);
|
加括号表示p_ps
是一个指针,它指向一个包含3个元素的数组。
使用typedef简化函数指针
使用typedef
可声明函数指针的别名,使代码更容易理解。如下所示。
1
| typedef int (*p_fun)(int, int);
|
这样p_fun
就可以看作一个数据类型来使用了,而之前的函数指针数组和指向函数指针数组的指针都可以使用如下方式声明。
1 2 3
| p_fun p1 = s1; p_fun pa[3] = {s1, s2, s3}; p_fun (*pd)[3] = &pa;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> using namespace std;
int s1(int a, int b) { return a + b + 0; } int s2(int a, int b) { return a + b + 1; } int s3(int a, int b) { return a + b + 2; }
int main() { int (*p_s[3])(int, int) = { s1, s2, s3};
for(int i = 0; i < 3; ++i) { cout << p_s[i](1, 2) << " "; }
int (*(*p_ps)[3])(int, int) = &p_s; for(int i = 0; i < 3; ++i) { cout << (*p_ps)[i](1, 2) << " "; }
}
|
使用using简化函数指针
c++11
增加了using
关键字,也可以用来取别名,类似于typedef
。如下所示。
1
| using p_fun = void(*)(int a, int b);
|
这样写的好处是可以清楚地看到p_fun
的数据类型便是右边的表达式,将名称与类型完全分离开了。