C语言函数详解:从基础概念到高级应用

C语言函数详解:从基础概念到高级应用

函数是C语言编程的核心概念之一,它允许我们将代码组织成可重用的模块。理解函数的工作原理对于编写高效、可维护的C程序至关重要。

一、函数基本概念

什么是函数?
函数是一段可以重复执行的代码块,它可以接受不同的参数,完成对应的操作,并可能返回一个结果。

函数声明示例:

C
1
2
3
int plus_one(int n) {
return n + 1;
}

函数声明的四个关键要素:

  1. 返回值类型:函数声明时首先需要给出返回值的类型,上例是int,表示函数返回一个整数
  2. 参数列表:函数名后面的圆括号里面声明参数的类型和参数名
  3. 函数体:函数体写在大括号里面,包含具体的执行代码
  4. return语句:给出函数的返回值,程序运行到这一行会结束函数调用

函数调用示例:

C
1
int a = plus_one(13);  // a 等于 14

参数个数必须匹配:

C
1
2
3
4
5
6
7
8
9
10
int plus_one(int n) {
return n + 1;
}

// 正确的调用
plus_one(5); // 正确

// 错误的调用
plus_one(2, 2); // 报错:参数过多
plus_one(); // 报错:参数过少

函数必须先声明后使用:

C
1
2
3
4
5
6
// 错误:在声明前使用函数
int a = plus_one(13);

int plus_one(int n) {
return n + 1;
}

void函数示例:

C
1
2
3
4
void myFunc(void) {
// 没有返回值,也不需要参数
printf("这是一个void函数\n");
}

递归函数示例(斐波那契数列):

C
1
2
3
4
5
6
unsigned long Fibonacci(unsigned n) {
if (n > 2)
return Fibonacci(n - 1) + Fibonacci(n - 2);
else
return 1;
}

二、main()函数

main()函数是C程序的入口点,所有的程序必须包含一个main()函数。

标准main()函数写法:

C
1
2
3
4
5
6
#include <stdio.h>

int main(void) {
printf("Hello World\n");
return 0;
}

返回值约定:

  • return 0:表示程序运行成功
  • 非零返回值:表示程序运行失败或出现问题

省略return的main()函数:

C
1
2
3
4
int main(void) {
printf("Hello World\n");
// 编译器会自动添加 return 0;
}

带参数的main()函数:

C
1
2
3
4
5
6
7
int main(int argc, char* argv[]) {
printf("参数个数: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("参数 %d: %s\n", i, argv[i]);
}
return 0;
}

三、参数的传值引用

C语言中,函数参数传递默认是传值引用,即传入的是变量值的拷贝。

传值引用的特点:

C
1
2
3
4
5
6
7
8
9
10
void increment(int a) {
a++; // 修改的是拷贝,不影响原始变量
}

int main() {
int i = 10;
increment(i);
printf("%d\n", i); // 输出10,i的值没有改变
return 0;
}

通过返回值修改变量:

C
1
2
3
4
5
6
7
8
9
10
11
int increment(int a) {
a++;
return a; // 返回修改后的值
}

int main() {
int i = 10;
i = increment(i); // 接收返回值
printf("%d\n", i); // 输出11
return 0;
}

交换变量的错误实现:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
void Swap(int x, int y) {
int temp;
temp = x;
x = y;
y = temp;
}

int main() {
int a = 1, b = 2;
Swap(a, b); // 无效,a和b的值不会交换
printf("a=%d, b=%d\n", a, b); // 输出a=1, b=2
return 0;
}

通过指针实现变量交换:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
void Swap(int* x, int* y) {
int temp;
temp = *x;
*x = *y;
*y = temp;
}

int main() {
int a = 1, b = 2;
Swap(&a, &b); // 传入地址
printf("a=%d, b=%d\n", a, b); // 输出a=2, b=1
return 0;
}

不要返回局部变量的指针:

C
1
2
3
4
5
// 错误示例
int* f(void) {
int i = 10;
return &i; // 危险!函数结束后i的内存无效
}

四、函数指针

函数指针是指向函数的指针变量,可以动态调用不同的函数。

函数指针声明:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
void print(int a) {
printf("%d\n", a);
}

int main() {
// 声明函数指针
void (*print_ptr)(int) = &print;

// 通过函数指针调用函数
(*print_ptr)(10); // 等同于 print(10)

return 0;
}

函数名的本质:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void print(int a) {
printf("%d\n", a);
}

int main() {
// print和&print是等价的
if (print == &print) {
printf("函数名本身就是指针\n");
}

// 多种调用方式
print(10); // 写法一
(*print)(10); // 写法二
(&print)(10); // 写法三

return 0;
}

函数指针作为参数:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

int add(int a, int b) {
return a + b;
}

int subtract(int a, int b) {
return a - b;
}

int compute(int (*func)(int, int), int x, int y) {
return func(x, y);
}

int main() {
int result1 = compute(add, 5, 3); // 8
int result2 = compute(subtract, 5, 3); // 2

printf("加法结果: %d\n", result1);
printf("减法结果: %d\n", result2);

return 0;
}

五、函数原型

函数原型允许函数先使用后声明,提高代码的可读性。

函数原型的使用:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

// 函数原型声明
int twice(int num);

int main() {
int result = twice(5); // 在声明前使用
printf("结果: %d\n", result);
return 0;
}

// 函数的具体实现
int twice(int num) {
return 2 * num;
}

函数原型的写法:

C
1
2
3
4
5
// 简洁写法
int twice(int);

// 详细写法(推荐)
int twice(int num);

六、exit()函数

exit()函数用于立即终止整个程序的运行。

exit()函数的使用:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>

int main() {
printf("程序开始运行\n");

// 程序运行成功
exit(EXIT_SUCCESS); // 等同于 exit(0)

// 这行代码不会执行
printf("这行不会执行\n");

return 0;
}

atexit()函数:注册退出处理函数

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

void cleanup(void) {
printf("执行清理工作...\n");
}

int main() {
// 注册退出处理函数
atexit(cleanup);

printf("程序运行中...\n");

// 程序异常终止
exit(EXIT_FAILURE); // 会先调用cleanup()

return 0;
}

七、函数说明符

1. extern说明符

C
1
2
3
4
5
6
7
// 在多文件项目中,声明外部函数
extern int external_function(int arg);

int main() {
int result = external_function(10);
return 0;
}

2. static说明符

static局部变量:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

void counter(void) {
static int count = 1; // 只初始化一次
printf("计数: %d\n", count);
count++;
}

int main() {
counter(); // 输出: 计数: 1
counter(); // 输出: 计数: 2
counter(); // 输出: 计数: 3
counter(); // 输出: 计数: 4
return 0;
}

static函数:

C
1
2
3
4
// 只能在当前文件内使用
static int internal_function(int num) {
return num * 2;
}

3. const说明符

保护指针指向的值:

C
1
2
3
4
void print_string(const char* str) {
// str[0] = 'A'; // 错误:不能修改const指针指向的值
printf("%s\n", str);
}

保护指针本身:

C
1
2
3
4
void process_data(int* const ptr) {
*ptr = 100; // 可以修改指向的值
// ptr = NULL; // 错误:不能修改指针本身
}

同时保护指针和指向的值:

C
1
2
3
4
5
void read_only(const int* const ptr) {
// *ptr = 100; // 错误
// ptr = NULL; // 错误
printf("值: %d\n", *ptr);
}

八、可变参数

可变参数函数可以接受不定数量的参数。

可变参数函数声明:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdarg.h>

// 计算多个数的平均值
double average(int count, ...) {
double total = 0;
va_list ap; // 可变参数对象

va_start(ap, count); // 初始化可变参数

for (int i = 0; i < count; i++) {
total += va_arg(ap, double); // 获取下一个参数
}

va_end(ap); // 清理可变参数对象

return total / count;
}

int main() {
double avg1 = average(3, 1.0, 2.0, 3.0);
double avg2 = average(5, 10.0, 20.0, 30.0, 40.0, 50.0);

printf("平均值1: %.2f\n", avg1); // 2.00
printf("平均值2: %.2f\n", avg2); // 30.00

return 0;
}

printf风格的格式化函数:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdarg.h>

void my_printf(const char* format, ...) {
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}

int main() {
my_printf("整数: %d, 浮点数: %.2f, 字符串: %s\n",
100, 3.14159, "Hello");
return 0;
}

九、函数最佳实践

  1. 单一职责原则:每个函数只完成一个明确的任务
  2. 合理的函数长度:函数不宜过长,建议不超过50行
  3. 有意义的函数名:函数名应该清晰表达其功能
  4. 适当的参数数量:避免参数过多,考虑使用结构体
  5. 错误处理:对可能的错误情况进行处理
  6. 文档注释:为函数添加清晰的注释说明

示例:良好的函数设计

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdbool.h>

/**
* 检查数字是否为质数
* @param number 要检查的数字
* @return true如果是质数,false如果不是质数
*/
bool is_prime(int number) {
if (number < 2) return false;
if (number == 2) return true;
if (number % 2 == 0) return false;

for (int i = 3; i * i <= number; i += 2) {
if (number % i == 0) return false;
}

return true;
}

/**
* 打印指定范围内的所有质数
* @param start 起始数字
* @param end 结束数字
*/
void print_primes_in_range(int start, int end) {
printf("在%d到%d之间的质数:\n", start, end);

for (int i = start; i <= end; i++) {
if (is_prime(i)) {
printf("%d ", i);
}
}
printf("\n");
}

int main() {
print_primes_in_range(1, 50);
return 0;
}

总结

函数是C语言编程的基石,掌握函数的使用对于编写高质量的C程序至关重要。从基本的函数声明和调用,到高级的函数指针和可变参数,理解这些概念将大大提升你的编程能力。记住良好的函数设计原则,编写出清晰、可维护的代码。


C语言函数详解:从基础概念到高级应用
https://www.edenzeng.online/2015/10/19/0.技术栈/01.开发语言/01.C语言/07-函数详解/
作者
Edenzeng
发布于
2015年10月19日
许可协议