函数的类型与地址
我们来看下面这个典型的函数:
int foo()
{
return 5;
}
显然foo
是函数名,而int
是函数返回值的类型。但是,函数有类型吗?有,函数有自己的类型,比如上面这个函数的类型即为“无参数且返回类型为整型”的函数。我们可以这么表示这种类型int (*somefunction)()
,同样的,如果是“有两个整形参数且返回值是布尔型”的我们可以这么表示bool (*someotherfunction)(int, int)
以函数类型的角度入手更容易理解
和变量一样,函数在内存中有固定的地址。函数的实质也是内存中一块固定的空间。
比如,当我这样调用函数foo()
:
cout << reinterpret_cast<void*>(foo);
或者这样:
std::cout<<foo; //在我的机器上打印出来的是数字1,而不是地址,
// 也有说是true的。
// 如果你用python,直接打印函数名(不带括号),出来的肯定是地址
结果是内存中的一个地址:0x103f87e20(具体取决于你的编译器)
函数指针
对于变量我们可以用int *a
这样的语法创建一个指针,如果我们想写一个指向函数的指针我们可以这么写:
int(*funcPtr)(); //函数指针
int (*const funcPtr)(); //或者我们也可以这么写,如果你需要一个静态的函数指针
s//另外,对于 const int(*funcPtr),意思是这个指针指向的函数的返回值是常量
把一个函数赋值给函数指针
int foo()
{
return 5;
}
int goo()
{
return 6;
}
int main()
{
int (*funcPtr)() = foo; // funcPtr 现在指向了函数foo
funcPtr = goo; // funcPtr 现在又指向了函数goo
//但是千万不要写成funcPtr = goo();这是把goo的返回值赋值给了funcPtr
return 0;
}
再来一波函数练习一下
int foo();
double goo();
int hoo(int x);
// 给函数指针赋值
int (*funcPtr1)() = foo; // 可以
int (*funcPtr2)() = goo; // 错误!返回值不匹配!
double (*funcPtr4)() = goo; // 可以
funcPtr1 = hoo; // 错误,因为参数不匹配,funcPtr1只能指向不含参数的函数,而hoo含有int型的参数
int (*funcPtr3)(int) = hoo; // 可以,所以应该这么写
另外,可以这么写
int foo(){
return 5;
}
int main()
{
int (*funcPtr1)() = foo;
int (*funcPtr2)() = &foo; // c++会隐式得把foo转换成&foo,所以你无需再加入&
std::cout << funcPtr1() << std::endl;
std::cout << funcPtr2() << std::endl;
}
结果:
5
5
通过函数指针调用函数
int foo(int x)
{
return x;
}
int main()
{
int (*funcPtr)(int) = foo;
(*funcPtr)(5); // 通过funcPtr调用foo(5)
funcPtr(5) // 也可以这么使用,在一些古老的编译器上可能不行
return 0;
}
把函数作为参数传入另一个函数
第一个栗子
#include <iostream>
int add(int a, int b){
return a+b;
}
int sub(int a, int b){
return a-b;
}
void func(int e, int d, int(*f)(int a, int b)){ // 重点:把函数指针作为参数,即将函数作为参数
std::cout<<f(e,d)<<std::endl;
}
int main()
{
func(2,3,add); // 传入函数名
func(2,3,sub);
return 0;
}
第二个栗子
首先我们来看这个简单的冒泡排序
#include <iostream>
template<typename T>
void bubblesort(T *a, int n){
bool sorted = false;
while(!sorted){
sorted = true;
for(int i=0; i<n-1; i++)
if(a[i] > a[i+1]){
std::swap(a[i], a[i+1]);
sorted = false;
}
n--;
}
}
int main()
{
int a[8] = {5,2,5,7,1,-3,99,56};
bubblesort<int>(a, 8);
for(auto e:a)
std::cout << e << " ";
return 0;
}
// -3 1 2 5 5 7 56 99 [Finished in 0.4s]
我们用ascending和descending两个函数代替大小判断,这样在调用的时候就可以选择是升序或者降序排列了
#include <iostream>
template <typename T>
bool ascending(T x, T y) {
return x > y;
}
template <typename T>
bool descending(T x, T y) {
return x < y;
}
template<typename T>
void bubblesort(T *a, int n, bool(*cmpfunc)(T, T)){
bool sorted = false;
while(!sorted){
sorted = true;
for (int i=0; i<n-1; i++)
if (cmpfunc(a[i], a[i+1])) {
std::swap(a[i], a[i+1]);
sorted = false;
}
n--;
}
}
int main()
{
int a[8] = {5,2,5,7,1,-3,99,56};
int b[8] = {5,2,5,7,1,-3,99,56};
bubblesort<int>(a, 8, ascending);
for (auto e:a) std::cout << e << " ";
std::cout << std::endl;
bubblesort<int>(b, 8, descending);
for (auto e:b) std::cout << e << " ";
return 0;
}
// -3 1 2 5 5 7 56 99
// 99 56 7 5 5 2 1 -3 [Finished in 0.4s]
设置默认函数
void bubblesort(T *a, int n, bool(*cmpfunc)(T, T) = ascending)