文章

C语言的一些技巧

本文记录了一些C语言的一些技巧

C语言的一些技巧

C语言函数参数传递详解

本文档详细说明C语言中函数参数传递的各种方法及其应用场景。

目录

  1. 值传递
  2. 指针传递
  3. 数组传递
  4. 结构体传递
  5. 多级指针传递
  6. 默认参数模拟
  7. 可变参数传递
  8. 总结对比

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 副本 不定参数函数

最佳实践建议

  1. 优先使用指针传递处理需要修改的数据
  2. 大型结构体必须使用指针传递
  3. 对只读参数添加const修饰
  4. 数组传递始终需要附带长度参数
  5. 谨慎使用可变参数,避免类型安全问题

C 语言格式化字符串速查指南

目录

  1. 整数格式化
  2. 浮点数格式化
  3. 字符串格式化
  4. 其他类型
  5. 组合用法
  6. 注意事项
  7. 示例场景

整数格式化

格式符 说明 示例输入 示例输出
%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"

注意事项

  1. 缓冲区溢出 确保目标数组足够大(例如 char buffer[64])。

  2. 类型安全 格式符需与变量类型严格匹配:
    • %dint
    • %ffloat/double
    • %uunsigned int
  3. 平台差异 在嵌入式系统中,部分格式符可能受限制(如 %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"

通过此文档,您可以快速查询各类格式化需求。建议根据实际场景调整格式符和参数。

本文由作者按照 CC BY 4.0 进行授权