C语言函数参数传递详解
本文档详细说明C语言中函数参数传递的各种方法及其应用场景。
目录
- 值传递
- 指针传递
- 数组传递
- 结构体传递
- 多级指针传递
- 默认参数模拟
- 可变参数传递
- 总结对比
1. 值传递
定义
创建参数的副本传递给函数,原始变量不受修改
示例代码
1
2
3
4
5
6
7
8
9
| void add(int a, int b) {
a += b; // 仅修改副本
}
int main() {
int x = 5, y = 3;
add(x, y); // x仍为5,y仍为3
}
|
特点
- ✅ 数据安全
- ❌ 无法修改原始变量
- 💡 适用于小型数据
2. 指针传递
定义
传递变量地址,可直接操作内存
示例代码
```c
void real_add(int* a, int* b) {
*a += *b; // 修改原始数据
}
int main() {
int x = 5, y = 3;
real_add(&x, &y); // x变为8
}
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
|
### 注意事项
- 必须进行空指针检查
- 使用`const`保护数据:`void print(const int* data)`
---
## 3. 数组传递
### 定义
数组名自动退化为指针
### 示例代码
```c
void modifyArray(int arr[], int len) {
arr[0] = 100; // 修改原始数组
}
int main() {
int nums[3] = {1,2,3};
modifyArray(nums, 3); // nums[0]变为100
}
|
特殊形式
1
2
| // 多维数组传递
void matrix(int (*mat)[3], int rows) { /*...*/ }
|
4. 结构体传递
定义
推荐使用指针避免拷贝开销
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
| typedef struct {
int x;
int y;
} Point;
void movePoint(Point* p, int dx) {
p->x += dx;
}
int main() {
Point pos = {10,20};
movePoint(&pos, 5); // pos.x变为15
}
|
5. 多级指针传递
应用场景
修改指针本身
示例代码
1
2
3
4
5
6
7
8
| void allocMemory(int** ptr, int size) {
*ptr = malloc(size * sizeof(int));
}
int main() {
int* arr = NULL;
allocMemory(&arr, 10); // 分配内存
}
|
6. 默认参数模拟
C语言实现方法
使用函数重载或宏定义
示例代码
1
2
3
4
5
6
7
8
9
10
| // 方法1:函数重载
#define NUM_UP_DN(...) _NUM_UP_DN(__VA_ARGS__, 0)
void _NUM_UP_DN(uint8_t H, uint8_t M, uint8_t S, uint8_t cv) { /*...*/ }
// 方法2:可选参数结构体
typedef struct {
uint8_t H;
uint8_t M;
uint8_t cv;
} ConfigParams;
|
7. 可变参数传递
使用标准库
1
2
3
4
5
6
7
8
9
10
11
12
13
| #include <stdarg.h>
void debugPrint(int count, ...) {
va_list args;
va_start(args, count);
for(int i=0; i<count; i++){
int val = va_arg(args, int);
printf("%d ", val);
}
va_end(args);
}
|
总结对比
| 传递方式 |
语法示例 |
内存操作 |
修改能力 |
适用场景 |
| 值传递 |
func(int a) |
副本 |
无 |
小型简单数据 |
| 指针传递 |
func(int* a) |
原始 |
有 |
需要修改变量 |
| 数组传递 |
func(int arr[]) |
原始 |
有 |
数组操作 |
| 结构体指针 |
func(Point* p) |
原始 |
有 |
复杂数据结构 |
| 多级指针 |
func(int** p) |
原始 |
有 |
动态内存分配 |
| 可变参数 |
va_list |
副本 |
无 |
不定参数函数 |
最佳实践建议:
- 优先使用指针传递处理需要修改的数据
- 大型结构体必须使用指针传递
- 对只读参数添加
const修饰
- 数组传递始终需要附带长度参数
- 谨慎使用可变参数,避免类型安全问题
C 语言格式化字符串速查指南
目录
- 整数格式化
- 浮点数格式化
- 字符串格式化
- 其他类型
- 组合用法
- 注意事项
- 示例场景
整数格式化
| 格式符 |
说明 |
示例输入 |
示例输出 |
%d |
基本十进制整数 |
5 |
5 |
%02d |
至少 2 位,不足补零 |
5 |
05 |
%4d |
右对齐,占 4 位 |
5 |
` 5` |
%-4d |
左对齐,占 4 位 |
5 |
5 |
%x |
十六进制(小写) |
255 |
ff |
%X |
十六进制(大写) |
255 |
FF |
%o |
八进制 |
8 |
10 |
浮点数格式化
| 格式符 | 说明 | 示例输入 | 示例输出 |
| ——- | ————————– | ——– | ————– |
| %.2f | 保留 2 位小数 | 3.1415 | 3.14 |
| %6.2f | 总宽度 6 位,保留 2 位小数 | 3.1415 | ` 3.14 |
| %e | 科学计数法(小写) | 1000 | 1.000000e+03 |
| %E | 科学计数法(大写) | 1000 | 1.000000E+03 |
| %g | 自动选择最短表示(小写) | 0.0001 | 1e-04` |
字符串格式化
| 格式符 |
说明 |
示例输入 |
示例输出 |
%s |
基本字符串输出 |
"Hello" |
Hello |
%.5s |
截取前 5 个字符 |
"Hello World" |
Hello |
%10s |
右对齐,占 10 位 |
"Hi" |
` Hi` |
%-10s |
左对齐,占 10 位 |
"Hi" |
Hi |
其他类型
| 格式符 |
说明 |
示例输入 |
示例输出 |
%p |
指针地址 |
&var |
0x7ffeeb0c |
%u |
无符号十进制 |
-1 (32-bit) |
4294967295 |
%% |
输出百分号 |
- |
% |
组合用法
固定宽度 + 补零
1
| sprintf(buffer, "%08.2f", 3.1); // → "00003.10"
|
动态宽度/精度
1
2
| int width = 5, precision = 2;
sprintf(buffer, "%*.*f", width, precision, 3.1415); // → " 3.14"
|
注意事项
-
缓冲区溢出
确保目标数组足够大(例如 char buffer[64])。
- 类型安全
格式符需与变量类型严格匹配:
%d → int
%f → float/double
%u → unsigned int
- 平台差异
在嵌入式系统中,部分格式符可能受限制(如
%f 需启用浮点支持)。
示例场景
1. 时间格式化(补零)
1
| sprintf(time_str, "%02d:%02d:%02d", hours, minutes, seconds); // → "09:05:08"
|
2. 日志输出
1
2
| float temperature = 23.456;
sprintf(log, "Temp: %5.1f°C", temperature); // → "Temp: 23.5°C"
|
3. 十六进制调试
1
2
| int error_code = 0xFF;
sprintf(debug, "Error: 0x%04X", error_code); // → "Error: 0x00FF"
|
通过此文档,您可以快速查询各类格式化需求。建议根据实际场景调整格式符和参数。