函数
普通函数
/* 标准形式
* <return type> <name>(<parameters>) {
* ...statement
* return <return value>;
* }
*
*/
int main(void) {
("Hello World");
puts
return 0;
}
// 多参数函数
double MultipleArg(double x, double y, double z) {
return x + y + z;
}
C 语言中的函数形式大致如上所示,由函数名、返回值、参数等组成。
其中,x
为形式参数,而像 2f
这样的为实际参数。
在 Google 的函数命名规范中,推荐使用 PascalCase
风格命名,即首字母大写,不同单词的首字母使用大写,例如
FindNumber
。
函数的原型
在阅读 C90 以前的代码时,你会发现有些函数不需要填写返回值。
() {
mainreturn 0;
} // default return int
如果没有声明函数返回值类型,默认为 int。
如果一个函数中不提供任何参数,则默认接受所有参数,例如:
#include <stdio.h>
#include <sys/time.h>
#include <stdint-gcc.h>
uint64_t GetCurrentTime() {
struct timeval tv;
(&tv,NULL);
gettimeofdayreturn tv.tv_sec * (uint64_t)1000000 + tv.tv_usec;
}
int main() {
(); // no error
GetCurrentTime(1); // no error
GetCurrentTime(1, "timezone"); // no error
GetCurrentTime}
因此,当表示不需要传参时,应当使用 void
表示,否则会造成不必要的麻烦。
函数声明
在 Java 中,我们知道你不能将欲调用的函数写在调用处的下面:
void printNumber() {
System.out.println(getNumber()); // error
}
double getNumber() {
return 114514.1919;
}
而在 C 中,它只关心函数的参数、名称及返回值,不关心具体实现。因此我们可以预先声明一个函数:
void Max(int, int);
// void Max(int num1, int num2) num1 and num2 are redundant
int main(void) {
(1, 2); // it works
Max}
void Max(int num1, int num2) {
// do something
}
我们将只含函数参数列表、函数名称及返回值的 “函数” 称为函数原型,且可以被用于函数声明。
你可以只声明函数原型,而在其他库中实现对应函数。
变长参数
在 C 语言中,也有像现代语言一样的变长参数,但支持比较原始,例如我们最熟悉的 printf 就使用了变长参数。一个支持变长参数的函数如下所示。
#include <stdio.h>
#include <stdarg.h>
void HandleVarargs(int arg_count, ...) {
va_list args; // 定义 va_list 获取变长参数
// 开始遍历
(args, arg_count);
va_start
for (int i = 0; i < arg_count; ++i) {
// 取出对应参数 (va_list, type)
int arg = va_arg(args, int);
("%d: %d\n", i, arg);
printf}
// 结束遍历
(args);
va_end}
需要注意的是,C 语言中的变长参数并不类型安全,你需要手动转换。
函数递归
递归主要由规则、初始值与终止条件组成,与数学中递归的概念相似。
我们以数学中的阶乘为例:
unsigned int Factorial(unsignedd int n) {
if (n == 0) {
return 1;
} else {
return n * Factorial(n - 1); // f(n) = n*f(n-1)
}
}
其中的终止条件即为 n = 0。
此外,还有最经典的例子——斐波那契数列:\(f(n) = f(n -1) + f(n-2)\)
以下是 C 中的实现:
unsigned int Fibonacci(unsigned int n) {
if (n == 1 || n == 0) {
return n;
} else {
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
}
但递归存在多层函数调用,内存开销很大,使用递归时应注意调用层数,或尝试使用迭代解决问题。
下面是阶乘与斐波那契的迭代实现:
unsigned int Factorial(unsigned int n) {
unsigned int result = 1;
for (unsigned int i = n; i > 0; --i) {
*= i;
result }
return result;
}
unsigned int Fibonacci(unsigned int n) {
if (n == 1 || n == 0) {
return n;
}
unsigned int last = 0;
unsigned int current = 1;
for (int i = 0; i <= n - 2; ++i) {
unsigned int temp = current;
+= last;
current = temp;
last }
return current;
}
迭代的实现显然易读性大大低于递归,但就性能上也大大优于递归,二者各有千秋。
在下一节中,我们将介绍 C 语言的头文件。