C/C++ 的函数

0. 函数调用

1. 函数声明

函数可以不声明,直接定义在主函数前面,这样可以被主函数正常调用,但是如果定义的函数去调用其他的自定义函数,就要注意函数的位置,函数只能调用定义在它前面的函数,不能调用定义在它后面的函数,这样的话,定义函数还要考虑它们定义的位置就比较麻烦了,所以声明函数就可以解决这个问题,声明可以写在所有函数的前面,也可以写在其他位置,但是如果声明在其它位置,那么这个函数还是不能被自由的调用,声明也就失去了它的意义,所以最好是将函数的声明写在所有函数的前面

2. 函数返回指针

函数的返回类型如果是指针变量,这个指针变量指向的空间一般应该是堆区的地址空间,因为如果返回的是指向栈区空间的指针,在函数执行结束后,这个栈区空间(局部变量)有可能生命周期已经结束,被系统释放掉了,虽然有时候可以正确访问到数据,但是很容易造成访问野指针的错误,因此,最好是返回指向堆区地址的指针,需要注意的一点是,返回堆区地址的指针用完之后,记得要释放掉,避免造成内存泄漏

3. 函数类型

int fun(int a, double b) {
  cout << a << " " << b << endl;
  return 0;
}

int main() {
  int a = 1;
  double b = 1.2;

  // 定义一个函数指针,满足三点要求返回值类型,参数类型和参数数量
  int (*p)(int, double) = fun;

  // 还可以这样写,与上面一条语句意义相同,因为 fun 和 &fun 都指向函数的首地址
  // 这与数组名的含义类似
  int (*q)(int, double) = &fun;

  // 以下 6 种方式均可成功调用函数
  fun(a, b);
  &fun(a, b);

  // p 指向的是函数 fun 的地址,p(a, b); 满足函数调用地址+参数列表的规则
  p(a, b);
  (*p)(a, b);
  q(a, b);

  // (*q)(a, b); q指向函数地址,(*q)就是函数本身, (*q)(a, b); 也就相当于 fun(a, b)
  // 也符合函数的调用规则
  (*q)(a, b);
}

4. 函数参数不确定的情况

形式:void fun(int n, …);

这样声明的函数可以实现参数个数不确定时的函数声明或定义,其中 n 表示参数的个数,后面三个点是可能的参数个数

void fun(int n, ...) {
  va_list ap;          // 定义一个保存参数的数组
  __va_start(&ap, n);  // 将参数装入数组中,ap 是保存参数的数组,n 是参数个数

  // 从参数数组中取数据,是按照参数类型往取的,且需要按照输入的顺序取
  cout << __crt_va_arg(ap, int) << endl;
  cout << __crt_va_arg(ap, double) << endl;
  cout << __crt_va_arg(ap, int) << endl;
}

int main() {
  int a = 2, b = 3;
  double c = 3.4;
  fun(3, a, c, b);
  system("pause");
  return 0;
}

注意

在取参数的时候是按照参数的类型取的,并且要与出入的参数顺序相同,比如第一个传 int,第二个传 double,取的时候也必须是第一个取 int,第二个取 double,在去参数的时候类似于出队列的操作,出一个就从队列中删除一个,但具体底层是如何实现的还不是很清楚,只知道与出队列的操作类似,FIFO原则

5. 传值 & 传址 & 传引用

传值

传址

传引用

Table of Contents