一种基于STM32 指定串口 自定义printf 的方法
```c#include <stdio.h>
#include <stdarg.h>
void UART_Printf(uint8_t index,const char* format, ...)
{
va_list args;
va_start(args, format);
int len = vsnprintf(NULL, 0, format, args);
va_end(args);
char* buffer = (char*)malloc(len + 1);
va_start(args, format);
vsnprintf(buffer, len + 1, format, args);
va_end(args);
//多出一个结束符'\0',可以不用打印
UART_Send(index,( uint8_t *) buffer,len );
free(buffer);
}
```
这个自定义printf函数的实现过程如下:
1. 首先,我们使用可变参数列表va_list类型的变量args,以及va_start函数来访问传递给myprintf函数的可变参数列表。
2. 然后,我们使用vsnprintf函数来计算格式化字符串的长度。vsnprintf函数会将格式化字符串格式化后的结果存储到一个缓冲区中,但是我们并不需要这个缓冲区的实际内容,所以将第一个参数设置为NULL,这样vsnprintf函数只会返回格式化字符串的长度。
3. 接着,我们使用malloc函数为缓冲区分配足够的内存。注意,由于vsnprintf函数返回的长度不包括字符串结尾的空字符'\0',所以我们需要在长度上加1。
4. 然后,我们再次使用va_start函数来访问可变参数列表,并使用vsnprintf函数将格式化后的字符串存储到缓冲区中。
5. 最后,我们使用UART_Send函数将缓冲区中的内容输出到指定串口中,并使用free函数释放缓冲区的内存。
需要注意的是,这只是一个简单的自定义printf函数的示例,实际中可能需要更多的错误检查和处理,以及更复杂的格式化字符串的处理。
**vsnprintf函数说明:**
vsnprintf是C标准库中的一个函数,它类似于sprintf函数,可以将格式化字符串格式化成一串字符并存储到一个缓冲区中。vsnprintf的一个重要特点是它允许指定一个缓冲区的大小,以避免缓冲区溢出的问题。
vsnprintf函数的函数原型如下:
```c
int vsnprintf(char* str, size_t size, const char* format, va_list ap);
```
其中,第一个参数str是一个指向缓冲区的指针,size是缓冲区的大小,format是一个字符串,用来指定输出的格式,ap是一个包含可变参数列表的va_list类型的变量。
vsnprintf函数会根据format参数指定的格式,将可变参数列表中的值转换成字符串,并将这些字符串存储到str指向的缓冲区中。如果str为NULL,则vsnprintf函数不会存储任何数据,但会计算出需要的缓冲区大小,并返回这个大小。
vsnprintf函数的返回值是一个int类型的值,表示实际写入缓冲区的字符数(不包括字符串结尾的空字符'\0'),或者是需要的缓冲区大小(如果str为NULL)。
需要注意的是,vsnprintf函数在向缓冲区写入数据时,会自动在输出的字符串末尾添加一个空字符'\0',因此在使用vsnprintf函数之前,必须确保缓冲区有足够的空间来存储这个空字符。
下面是一个使用vsnprintf函数的示例代码:
```c
#include <stdio.h>
#include <stdarg.h>
int main()
{
char buffer;
int len = vsnprintf(buffer, 100, "The answer is %d.", 42);
printf("vsnprintf returned %d\n", len);
printf("buffer contains: %s\n", buffer);
return 0;
}
```
在这个示例中,我们定义了一个大小为100的缓冲区,然后使用vsnprintf函数将格式化字符串"The answer is %d."格式化成一串字符,并存储到缓冲区中。由于这个字符串的长度为16,加上一个空字符'\0'一共需要17个字符的空间,而缓冲区的大小为100,因此这个操作不会导致缓冲区溢出。最后,我们输出vsnprintf函数的返回值和缓冲区的内容。
**va_start是什么,什么是可变参数列表**
va_start是C语言标准库中的一个宏定义,用于初始化一个指向可变参数列表的变量。在使用可变参数列表时,必须先使用va_start宏来初始化一个va_list类型的变量,这个变量包含了可变参数列表中的所有参数,以便后续的操作中使用。
可变参数列表是一种特殊的函数参数列表,它允许函数接受任意数量和任意类型的参数。
例如,我们可以编写一个函数,接受任意数量的整数作为参数,并返回它们的平均值:
```c
#include <stdarg.h>
double average(int count, ...)
{
va_list args;
va_start(args, count);
double sum = 0;
for (int i = 0; i < count; i++) {
int arg = va_arg(args, int);
sum += arg;
}
va_end(args);
return sum / count;
}
int main()
{
double result = average(3, 1, 2, 3);
printf("The average is %f\n", result);
return 0;
}
```
在这个示例中,我们使用可变参数列表来接受任意数量的整数参数,并计算它们的平均值。在average函数中,我们首先使用va_start宏初始化一个va_list类型的变量args,然后使用for循环遍历前count个参数,并使用va_arg宏来从可变参数列表中依次读取参数。最后,我们使用va_end宏来清理va_list类型的变量args。
这个示例只是一个简单的例子,实际中可变参数列表常用于处理不同类型和数量的参数,例如printf函数。需要注意的是,使用可变参数列表时,必须始终提供一个固定参数,以便确定可变参数列表的起始地址。
逸邦 发表于 2023-6-27 15:51
有没有可能直接重定义printf函数呢?
直接重定义的话,相对来说,不够灵活,我碰到的问题,主要在以下两点:
1.没有办法实现指定串口,只能固定某个串口使用printf
2.无法在打印前后,进行一些额外的处理。
举例①:例如RS485打印,如果不能在printf函数内部进行处理的话,我就需要这样写
RS485_TX_EN;
printf();
RS485RX_EN;
假如可以在函数内部处理的话,只需要写一个printf就可以了,相对来说,更方便。
举例② :在打印前后,添加字符前缀
例如,我需要根据打印信息进行分类,使用error_printf,默认在前面加上error: ,
签到 有没有可能直接重定义printf函数呢?
签到
小白云 发表于 2023-6-27 15:57
直接重定义的话,相对来说,不够灵活,我碰到的问题,主要在以下两点:
1.没有办法实现指定串口,只 ...
无法使用MicroLIB,会导致vsnprintf截断
无法使用int len = vsnprintf(NULL, 0, format, args);获取到字符长度。
页:
[1]