欢迎光临
我们一直在努力

STM32开发中那些可正常调用但极少使用的C标准冷门函数

STM32开发中那些可正常调用但极少使用的C标准冷门函数

    • 1、 引言
    • 2、为什么关注冷门函数?
      • 2.1、 资源受限环境下的优化
      • 2.2、 避免重复造轮子
      • 2.3 、代码可移植性
    • 3、 字符串处理冷门函数
      • 3.1、 `strxfrm()` – 字符串转换函数
      • 3.2、 `strcoll()` – 区域设置相关的字符串比较
    • 4、 内存操作冷门函数
      • 4.1、 `memccpy()` – 带终止字符的内存复制
      • 4.2、 `wmemcpy()` / `wmemmove()` – 宽字符内存操作
    • 5、 数学函数中的冷门函数
      • 5.1、 `remquo()` – 同时计算余数和商
      • 5.2 、`nextafter()` / `nexttoward()` – 获取相邻浮点数
    • 6、输入输出冷门函数
      • 6.1、 `ungetc()` – 将字符推回输入流
    • 7、 实用但少用的工具函数
      • 7.1、 `div()` / `ldiv()` / `lldiv()` – 同时计算商和余数
      • 7.2、 `bsearch()` – 二分查找
      • 7.3、`qsort` -快速排序
    • 8、 在STM32项目中使用的建议
      • 8.1 、需要考虑的因素
      • 8.2、 推荐使用场景
    • 9、 结论
  • 二、代码示例
    • 1、示例代码
    • 2、运行结果

在这里插入图片描述

1、 引言

在STM32嵌入式开发中,我们经常使用C标准库中的常见函数,如printf、malloc、strcpy等。然而,C标准库中还存在一些"冷门"函数,它们在STM32上可以正常调用,但由于使用场景特殊或功能重叠,很少被开发者注意到。本文将介绍这些函数,探讨它们的适用场景,并分析在STM32项目中是否值得使用。

2、为什么关注冷门函数?

2.1、 资源受限环境下的优化

STM32作为嵌入式微控制器,通常运行在资源受限的环境中。了解这些冷门函数可能带来:

  • 更小的代码体积
  • 更高的执行效率
  • 更少的内存占用

2.2、 避免重复造轮子

许多开发者会自己实现某些功能,而标准库中可能已有现成的、经过充分测试的实现。

2.3 、代码可移植性

使用标准函数而非自定义实现,可以提高代码在不同平台间的可移植性。

3、 字符串处理冷门函数

3.1、 strxfrm() – 字符串转换函数

#include <string.h>

size_t strxfrm(char *dest, const char *src, size_t n);

功能:根据当前区域设置转换字符串,用于本地化字符串比较。

STM32适用性:

  • 在需要多语言支持的嵌入式界面中可能有用
  • 但STM32项目通常使用固定编码,很少需要本地化
  • 函数实现较复杂,可能增加代码体积

3.2、 strcoll() – 区域设置相关的字符串比较

#include <string.h>

int strcoll(const char *s1, const char *s2);

功能:根据当前区域设置比较两个字符串。

使用示例:

// 在STM32上可以正常调用,但很少使用
const char *str1 = "äpple";
const char *str2 = "apple";
int result = strcoll(str1, str2); // 考虑区域设置的比较

4、 内存操作冷门函数

4.1、 memccpy() – 带终止字符的内存复制

#include <string.h>

void *memccpy(void *dest, const void *src, int c, size_t n);

功能:从src复制最多n个字节到dest,如果遇到字符c则停止。

STM32适用性分析:

// 传统做法
char buffer[100];
const char *source = "Hello,World,Test";
char *comma = strchr(source, ',');
size_t len = comma ? (comma – source) : strlen(source);
memcpy(buffer, source, len);
buffer[len] = '\\0';

// 使用memccpy
memccpy(buffer, source, ',', sizeof(buffer));
// 自动在逗号处停止,但不会自动添加'\\0'

优缺点:

  • 优点:在某些场景下代码更简洁
  • 缺点:不会自动添加字符串终止符,容易造成bug

4.2、 wmemcpy() / wmemmove() – 宽字符内存操作

#include <wchar.h>

wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, size_t n);
wchar_t *wmemmove(wchar_t *dest, const wchar_t *src, size_t n);

STM32适用性:

  • 在需要Unicode支持的显示系统中可能有用
  • 但STM32项目通常使用UTF-8或ASCII,很少使用宽字符
  • 增加代码体积和内存占用

5、 数学函数中的冷门函数

5.1、 remquo() – 同时计算余数和商

#include <math.h>

double remquo(double x, double y, int *quo);
float remquof(float x, float y, int *quo);
long double remquol(long double x, long double y, int *quo);

功能:计算浮点数除法的余数,同时通过指针返回商。

STM32使用场景:

// 在需要同时获取商和余数的DSP算法中
float dividend = 10.5f;
float divisor = 3.2f;
int quotient;
float remainder = remquof(dividend, divisor, &quotient);

// 等价于:
// float remainder = fmodf(dividend, divisor);
// int quotient = (int)(dividend / divisor);
// 但remquof可能更精确,因为商和余数是一次计算出来的

5.2 、nextafter() / nexttoward() – 获取相邻浮点数

#include <math.h>

double nextafter(double x, double y);
float nextafterf(float x, float y);

功能:返回x在趋向y方向上的下一个可表示的浮点数。

适用场景:

  • 数值分析算法
  • 浮点数误差分析
  • 但STM32嵌入式开发中很少需要

6、输入输出冷门函数

6.1、 ungetc() – 将字符推回输入流

#include <stdio.h>

int ungetc(int c, FILE *stream);

STM32适用性:

// 在自定义解析器中可能有用
int parse_number(FILE *stream) {
int c = fgetc(stream);
if (!isdigit(c)) {
ungetc(c, stream); // 不是数字,放回流中
return –1;
}
// … 解析数字
}

注意事项:

  • 在STM32上,FILE流通常对应UART或USB
  • 推回字符的实现可能依赖缓冲区
  • 在无缓冲或小缓冲区系统中可能不可靠

7、 实用但少用的工具函数

7.1、 div() / ldiv() / lldiv() – 同时计算商和余数

#include <stdlib.h>

div_t div(int numer, int denom);
ldiv_t ldiv(long numer, long denom);
lldiv_t lldiv(long long numer, long long denom);

优点:

  • 一次调用同时获得商和余数
  • 在某些架构上可能比分别计算更高效

STM32示例:

// 传统做法
int quotient = a / b;
int remainder = a % b;

// 使用div
div_t result = div(a, b);
// result.quot 是商
// result.rem 是余数

7.2、 bsearch() – 二分查找

#include <stdlib.h>

void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));

虽然bsearch不是特别冷门,但在STM32嵌入式开发中,由于数据量通常不大,开发者更倾向于使用线性查找。

7.3、qsort -快速排序

void qsort(
void *base, // 待排序数组首地址
size_t nmemb, // 数组元素个数
size_t size, // 单个元素大小(字节)
int (*compar)(const void *, const void *) // 比较函数指针
);

qsort 是 stdlib 标准通用快排,STM32 裸机可直接调用,适配任意数据类型,依靠自定义回调函数实现升降序,嵌入式因栈开销大多手写排序替代。 示例:

#include <stdio.h>
#include <stdlib.h>

// 升序比较函数
int cmp_ascend(const void *a, const void *b)
{
return *(int*)a – *(int*)b;
}

// 降序比较函数
int cmp_descend(const void *a, const void *b)
{
return *(int*)b – *(int*)a;
}

int main(void)
{
int arr[] = {5, 2, 9, 1, 5, 6};
int num = sizeof(arr) / sizeof(int);

// 升序排序
qsort(arr, num, sizeof(int), cmp_ascend);

// 降序排序
// qsort(arr, num, sizeof(int), cmp_descend);

// 打印结果
for(int i=0; i<num; i++)
{
printf("%d ", arr[i]);
}

return 0;
}

8、 在STM32项目中使用的建议

8.1 、需要考虑的因素

  • 代码体积

    • 使用冷门函数可能引入不常用的库代码
    • 使用arm-none-eabi-size工具检查影响
  • 执行效率

    • 某些冷门函数可能针对特定场景优化
    • 但通用函数通常已经过充分优化
  • 可读性和可维护性

    • 团队成员可能不熟悉这些函数
    • 增加代码理解成本
  • 可移植性

    • 标准函数在不同编译器间行为一致
    • 但某些冷门函数可能不是所有编译器都完全支持
  • 8.2、 推荐使用场景

    函数推荐使用场景替代方案
    memccpy() 解析分隔符分隔的数据 strchr() + memcpy()
    div() 需要同时获取商和余数的整数运算 分别使用/和%
    bsearch() 大型静态查找表 线性查找或自定义哈希
    strxfrm() 多语言UI的字符串排序 固定编码的strcmp()

    9、 结论

    STM32开发中确实存在许多可以正常调用但极少使用的C标准冷门函数。这些函数在某些特定场景下可能提供优势,但在大多数嵌入式应用中,使用更常见、更易理解的函数通常是更好的选择。

    关键建议:

  • 优先使用常见函数,除非冷门函数能带来明显优势
  • 进行基准测试,验证性能提升是否值得
  • 添加充分注释,说明为什么选择冷门函数
  • 考虑团队熟悉度,避免增加维护成本
  • 在资源受限的STM32环境中,代码的清晰性和可维护性往往比微小的性能优化更重要。只有在经过充分测试和评估后,才应考虑使用这些冷门函数。

    二、代码示例

    1、示例代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    // ====================== 示例1:int 数组排序 + 查找 ======================
    // int 升序比较函数(给 qsort / bsearch 共用)
    int cmp_int(const void* a, const void* b)
    {
    return *(int*)a – *(int*)b;
    }

    void test_int_qsort_bsearch(void)
    {
    int arr[] = { 12, 3, 56, 7, 22, 49 };
    int num = sizeof(arr) / sizeof(arr[0]);
    int key = 22; // 要查找的值
    int* find_p;

    printf("===== 示例1:int 数组 =====\\n");

    // 1. 先排序(必须排序!bsearch 只支持有序数组)
    qsort(arr, num, sizeof(int), cmp_int);

    printf("排序后:");
    for (int i = 0; i < num; i++) {
    printf("%d ", arr[i]);
    }
    printf("\\n");

    // 2. 二分查找
    find_p = (int*)bsearch(&key, arr, num, sizeof(int), cmp_int);

    if (find_p != NULL) {
    printf("找到元素:%d\\n\\n", *find_p);
    }
    else {
    printf("未找到元素\\n\\n");
    }
    }

    // ====================== 示例2:结构体数组(嵌入式常用) ======================
    // 设备信息结构体
    typedef struct {
    int id; // 设备ID(按此字段排序)
    char name[16]; // 设备名
    } Device_T;

    // 按 id 升序比较
    int cmp_device(const void* a, const void* b)
    {
    return ((Device_T*)a)->id – ((Device_T*)b)->id;
    }

    void test_struct_qsort_bsearch(void)
    {
    // 乱序设备列表
    Device_T devs[] = {
    {5, "LED"},
    {2, "OLED"},
    {8, "KEY"},
    {1, "SENSOR"},
    {4, "MOTOR"}
    };

    int num = sizeof(devs) / sizeof(devs[0]);
    Device_T key = { 4, "" }; // 要查找的设备ID=4
    Device_T* find_p;

    printf("===== 示例2:结构体数组 =====\\n");

    // 1. 排序
    qsort(devs, num, sizeof(Device_T), cmp_device);

    printf("按ID排序后:\\n");
    for (int i = 0; i < num; i++) {
    printf("ID:%d Name:%s\\n", devs[i].id, devs[i].name);
    }

    // 2. 查找
    find_p = (Device_T*)bsearch(&key, devs, num, sizeof(Device_T), cmp_device);

    if (find_p != NULL) {
    printf("\\n找到设备:ID=%d, Name=%s\\n", find_p->id, find_p->name);
    }
    else {
    printf("\\n未找到设备\\n");
    }
    }

    // ====================== 主函数 ======================
    int main(void)
    {
    test_int_qsort_bsearch();
    test_struct_qsort_bsearch();
    return 0;
    }

    2、运行结果

    ===== 示例1:int 数组 =====
    排序后:3 7 12 22 49 56
    找到元素:22

    ===== 示例2:结构体数组 =====
    按ID排序后:
    ID:1 Name:SENSOR
    ID:2 Name:OLED
    ID:4 Name:MOTOR
    ID:5 Name:LED
    ID:8 Name:KEY

    找到设备:ID=4, Name=MOTOR

    D:\\user\\01417804\\桌面\\新建文件夹\\Project1\\x64\\Debug\\Project1.exe (进程 30176)已退出,代码为 0 (0x0)。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .

    在这里插入图片描述

    在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » STM32开发中那些可正常调用但极少使用的C标准冷门函数
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址