Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/apache-flex/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如何在用户定义函数中使用格式化字符串?_C_Printf_Stm32_Lcd - Fatal编程技术网

C 如何在用户定义函数中使用格式化字符串?

C 如何在用户定义函数中使用格式化字符串?,c,printf,stm32,lcd,C,Printf,Stm32,Lcd,我想编写一个函数,以类似printf/sprintf使用格式化字符串的方式在LCD上打印字符 您可以使用sprintf函数格式化字符串并打印到LCD char buffer[50]; int a = 10, b = 20, c; c = a + b; sprintf(buffer, "Sum of %d and %d is %d", a, b, c); 现在,缓冲区将具有格式化字符串您可以编写一个可变函数,并将参数传递到vsnprintf(): #包括 #包括 #包括 无效显示

我想编写一个函数,以类似printf/sprintf使用格式化字符串的方式在LCD上打印字符

您可以使用sprintf函数格式化字符串并打印到LCD

 char buffer[50]; 
 int a = 10, b = 20, c; 
 c = a + b; 
 sprintf(buffer, "Sum of %d and %d is %d", a, b, c); 

现在,
缓冲区将具有格式化字符串

您可以编写一个可变函数,并将参数传递到
vsnprintf()

#包括
#包括
#包括
无效显示(int foo、int bar、char const*格式,…)
{
va_列表arglist;
va_开始(arglist,格式);
int length=vsnprintf(NULL,0,格式,arglist);
char*buffer=malloc(长度*sizeof*buffer);
vsnprintf(缓冲区、长度、格式、arglist);
va_end(arglist);
放置(缓冲);
自由(缓冲);
}
内部主(空)
{
显示(42,13,“%s%d%f”,“你好”,99100.13);
}

由于最常用的arm编译器是gcc,我将只关注这一个。编译器可以检查格式&参数与printf相同

\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

从gcc文档中

格式(原型、字符串索引、首先检查) format属性指定函数采用printf、scanf、strftime或strfmon样式的参数,这些参数应为 根据格式字符串检查类型。例如,声明:

          extern int
          my_printf (void *my_object, const char *my_format, ...)
                __attribute__ ((format (printf, 2, 3)));


causes the compiler to check the arguments in calls to my_printf for consistency with the printf style format string argument
          extern char *
          my_dgettext (char *my_domain, const char *my_format)
                __attribute__ ((format_arg (2)));


causes the compiler to check the arguments in calls to a printf, scanf, strftime or strfmon type function, whose format string argument
我的格式

The parameter archetype determines how the format string is interpreted, and should be printf, scanf, strftime or strfmon. (You
也可以使用printfscanfstrftimestrfmon)命令 参数字符串索引指定哪个参数是格式字符串 参数(从1开始),而首先要检查的是 检查格式字符串的第一个参数。对于函数,其中 无法检查参数(如vprintf), 将第三个参数指定为零。在这种情况下,仅编译器 检查格式字符串的一致性。对于strftime格式 第三个参数要求为零

In the example above, the format string (my_format) is the second argument of the function my_print, and the arguments to check start
使用第三个参数,因此格式的参数是正确的 属性为2和3

The format attribute allows you to identify your own functions which take format strings as arguments, so that GCC can check the
调用这些函数以查找错误。编译器始终(除非 -f使用重建)检查标准库函数printf、fprintf、sprintf、scanf、fscanf、sscanf、strftime、, 每当请求此类警告时,vprintf、vfprintf和vsprintf (使用-Wformat),因此无需修改头文件 在C99模式下,函数snprintf、vsnprintf、vscanf、, 还检查了vfscanf和vsscanf。严格符合C的情况除外 在标准模式下,X/Open功能strfmon也会按原样进行检查 printf_解锁和fprinf_解锁。请参阅选项C 方言.格式参数(字符串索引) format_arg属性指定函数为printf、scanf、strftime或strfmon样式的函数和 修改它(例如,将其翻译成另一种语言),以便 结果可以传递给printf、scanf、strftime或strfmon样式 函数(与format函数的其余参数相同 与未修改的字符串相同)。例如 声明:

          extern int
          my_printf (void *my_object, const char *my_format, ...)
                __attribute__ ((format (printf, 2, 3)));


causes the compiler to check the arguments in calls to my_printf for consistency with the printf style format string argument
          extern char *
          my_dgettext (char *my_domain, const char *my_format)
                __attribute__ ((format_arg (2)));


causes the compiler to check the arguments in calls to a printf, scanf, strftime or strfmon type function, whose format string argument
是对my_dgettext函数的调用,以与格式保持一致 字符串参数my_format。如果尚未设置format_arg属性 指定时,编译器可以在此类格式化调用中分辨出的所有内容 函数应该是格式字符串参数不是常量; 这将在使用-Wformat nonliteral时生成警告,但是 如果没有该属性,则无法检查调用


这个答案把所有其他答案中最好的部分放在一起,我认为这是所有因素的最佳方式,并在给出例子后更详细地解释。

总结: 这是一个完整的示例,包括函数中的基本错误检查。在这里,我创建了一个类似于
printf
的函数,名为
lcd\u printf()
,它的工作原理与
printf()
完全相同。它使用
vsnprintf()
将格式化字符串存储到静态分配的缓冲区中。然后,您可以将此缓冲区发送到我的注释所示位置的LCD显示屏上

示例代码: lcd\u print.h:

// For info on the gcc "format" attribute, read here under the section titled 
// "format (archetype, string-index, first-to-check)": 
// https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/Common-Function-Attributes.html#Common-Function-Attributes.
int lcd_printf(const char * format, ...) __attribute__((format(printf, 1, 2)));
#include "lcd_print.h"

#include <stdarg.h> // for variable args: va_list
#include <stdio.h>  // for vsnprintf()
#include <limits.h> // for INT_MIN

// `printf`-like function to print to the LCD display.
// Returns the number of chars printed, or a negative number in the event of an error. 
// Error Return codes: 
//     1. INT_MIN if vsnprintf encoding error, OR
//     2. negative of the number of chars it *would have printed* had the buffer been large enough (ie: buffer would 
//     have needed to be the absolute value of this size + 1 for null terminator)
int lcd_printf(const char * format, ...)
{
    int return_code;

    // Formatted string buffer: make as long as you need it to be to hold the longest string you'd ever want 
    // to print + null terminator
    char formatted_str[128]; 

    va_list arglist;
    va_start(arglist, format);

    // Produce the formatted string; see vsnprintf documentation: http://www.cplusplus.com/reference/cstdio/vsnprintf/
    int num_chars_to_print = vsnprintf(formatted_str, sizeof(formatted_str), format, arglist); 
    va_end(arglist);

    if (num_chars_to_print < 0)
    {
        // Encoding error
        return_code = INT_MIN;
        return return_code; // exit early
    }
    else if (num_chars_to_print >= sizeof(formatted_str))
    {
        // formatted_str buffer not long enough
        return_code = -num_chars_to_print;
        // Do NOT return here; rather, continue and print what we can
    }
    else
    {
        // No error
        return_code = num_chars_to_print;
    }

    // Now do whatever is required to send the formatted_str buffer to the LCD display here.

    return return_code;
}
#include "lcd_print.h"

int main(void)
{
    int num1 = 7;
    int num2 = -1000;
    unsigned int num3 = 0x812A;

    lcd_printf("my 3 numbers are %i, %i, 0x%4X\n", num1, num2, num3);

    return 0;
}
lcd\u print.c:

// For info on the gcc "format" attribute, read here under the section titled 
// "format (archetype, string-index, first-to-check)": 
// https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/Common-Function-Attributes.html#Common-Function-Attributes.
int lcd_printf(const char * format, ...) __attribute__((format(printf, 1, 2)));
#include "lcd_print.h"

#include <stdarg.h> // for variable args: va_list
#include <stdio.h>  // for vsnprintf()
#include <limits.h> // for INT_MIN

// `printf`-like function to print to the LCD display.
// Returns the number of chars printed, or a negative number in the event of an error. 
// Error Return codes: 
//     1. INT_MIN if vsnprintf encoding error, OR
//     2. negative of the number of chars it *would have printed* had the buffer been large enough (ie: buffer would 
//     have needed to be the absolute value of this size + 1 for null terminator)
int lcd_printf(const char * format, ...)
{
    int return_code;

    // Formatted string buffer: make as long as you need it to be to hold the longest string you'd ever want 
    // to print + null terminator
    char formatted_str[128]; 

    va_list arglist;
    va_start(arglist, format);

    // Produce the formatted string; see vsnprintf documentation: http://www.cplusplus.com/reference/cstdio/vsnprintf/
    int num_chars_to_print = vsnprintf(formatted_str, sizeof(formatted_str), format, arglist); 
    va_end(arglist);

    if (num_chars_to_print < 0)
    {
        // Encoding error
        return_code = INT_MIN;
        return return_code; // exit early
    }
    else if (num_chars_to_print >= sizeof(formatted_str))
    {
        // formatted_str buffer not long enough
        return_code = -num_chars_to_print;
        // Do NOT return here; rather, continue and print what we can
    }
    else
    {
        // No error
        return_code = num_chars_to_print;
    }

    // Now do whatever is required to send the formatted_str buffer to the LCD display here.

    return return_code;
}
#include "lcd_print.h"

int main(void)
{
    int num1 = 7;
    int num2 = -1000;
    unsigned int num3 = 0x812A;

    lcd_printf("my 3 numbers are %i, %i, 0x%4X\n", num1, num2, num3);

    return 0;
}
对替代方法的解释和比较: @哈里克里希南。这是一条正确的道路,是一种有效的、但通用性较差且完整的方法。创建一个使用
vsnprintf()
的新方法,就像@swardfish和我所做的那样,效果更好

@箭鱼在正确使用
vsnprintf()
方面做了大量工作,以便创建自己的
printf()
-like。他的示例(除了缺少错误处理)是定制
printf()
-like实现的完美模板,该实现依赖于动态内存分配。他第一次调用
vsnprintf()
,带有一个
NULL
目标缓冲区,只不过决定他需要为格式化字符串分配多少字节(这是一个巧妙且常用的技巧),以及他对
vsnprintf()的第二次调用
实际创建格式化字符串。对于也有大量RAM的非实时应用程序(例如:PC应用程序),这是一种完美的方法。但是,对于微控制器,我强烈建议不要使用它,因为:

  • 它是不确定的。每次调用
    free()
    都可能需要不同的时间(而且事先无法确定)来完成。这是因为堆内存会随着时间的推移而变得碎片化。这意味着这种方法不适合实时系统。
    • 有关
      malloc()
      free()
      的各种堆实现的更多信息,请查看FreeRTOS在此处描述的5种堆实现:。在此页面中搜索“确定性”
  • 它是无界的。它将尝试
    malloc()
    ANY