【问题标题】:cost of fprintffprintf 的成本
【发布时间】:2011-03-02 09:39:41
【问题描述】:

我正在为一个代码/数据 RAM 有限但文件系统使用无限 RAM 的平台开发一个 C++ 嵌入式应用程序。

在寻找减少代码大小的过程中,我意识到排除 fprintf() 行对生成代码的大小有很大影响。

我的问题是: 1. 为什么fprintf的成本这么高? 2. 如果我排除了 fprintf 功能,还有什么替代方法来生成描述通过应用程序运行发生的日志文件?

【问题讨论】:

    标签: c++ c embedded printf


    【解决方案1】:

    在嵌入式系统中,printf 有时可以拖入对格式字符串的所有浮点支持,例如%f

    更智能的环境将使printf 的浮点选项成为可选的东西。

    但即使对于整数,printf 中也有很多通用代码,您可能会发现编写自己的例程更加紧凑,根据您的特定需求量身定制,例如:

    outInt (char *buff, int intVal);
    outChr (char *buff, char chVal);
    outStr (char *buff, char *strVal);
    

    以此类推,用于写入缓冲区,然后outBuff (char *buff) 用于将其发送到文件或标准输出。


    例如,如果您控制正在使用的数据(无字符串溢出、16 位二进制补码整数等),您可以使用以下函数:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void outChr (char *buff, char chVal) {
        *buff++ = chVal;
        *buff = '\0';
    }
    
    void outStr (char *buff, char *strVal) {
        strcpy (buff, strVal);
    }
    

     

    void outInt (char *buff, int intVal) {
        int divisor = 10000, printing = 0;
    
        // Special cases.
    
        if (intVal == -32768) { outStr (buff, "-32768"); return; }
        if (intVal ==      0) { outChr (buff,      '0'); return; }
    
        // Handle negatives.
    
        if (intVal < 0) { outChr (buff++, '-'); intVal = -intVal; }
    
        // Handle non-zero positives <= 32767.
    
        while (divisor > 0) {
            if ((intVal >= divisor) || printing) {
                outChr (buff++, "0123456789"[intVal/divisor]);
                printing = 1;
            }
            intVal = intVal % divisor;
            divisor /= 10;
        }
    }
    

     

    int main (int argc, char *argv[]) {
        char buff[1000];
        int i;
        for (i = 1; i < argc; i++) {
            outInt (buff, atoi (argv[i]));
            printf ("[%s] -> [%s]\n", argv[i], buff);
        }
        return 0;
    }
    

    运行这个:

    pax$ tstprg 32767 10000 9999 10 9 1 0 -1 -9 -10 -99 -10000 -32767 -32768
    

    输出:

    [32767] -> [32767]
    [10000] -> [10000]
    [9999] -> [9999]
    [10] -> [10]
    [9] -> [9]
    [1] -> [1]
    [0] -> [0]
    [-1] -> [-1]
    [-9] -> [-9]
    [-10] -> [-10]
    [-99] -> [-99]
    [-10000] -> [-10000]
    [-32767] -> [-32767]
    [-32768] -> [-32768]
    

    这些函数的大小应该相对较小,因为它们针对的是特定需求,而不是更通用的printf 系列。

    【讨论】:

      【解决方案2】:

      提供完全符合 ANSI 的printf 功能需要合理数量的代码。

      一些嵌入式环境提供了几个不同版本的printf,这些版本要小得多,因为它们只提供选定的功能。

      例如,IAR C/C++ Compiler for MSP430 (PDF) 提供 TinySmallLargeFull 的实现printf 格式化程序,Tiny 版本仅支持基本说明符 (c, d, i, o, p, s, u, X, x, and %),不支持多字节、浮点数、长度修饰符、宽度和精度。

      如果您的环境提供此选择,请选择符合您需求的printf(和scanf)版本并注意限制。

      如果您的环境不提供此选择,请查看各种 “tiny” 替代可用的 printf 实现(例如 this one from Kustaa Nyholm of SpareTimeLabs)。

      【讨论】:

        【解决方案3】:

        我能想到三种情况:

        1. 每次删除 fprintf 行时,代码大小都会略有下降,而当您删除最后一个 fprint 时,代码大小也会略有下降。
        2. 当您删除最后一个 fprint 时,代码大小会显着下降。
        3. 每次删除一个 fprint 实例,代码大小都会显着下降。

        在场景 1 中,罪魁祸首不是 fprintf,而是您传递给它的字符串文字。您需要从代码中取出这些字符串,方法是使用非常简短的消息,或者将字符串存储在文件中并通过代码中的某种形式的 ID 引用它们(尽管这会导致性能下降)

        在场景 2 中,fprintf (可能)是罪魁祸首。这是一个相当复杂的函数,能够以各种方式格式化各种数据类型 - 所以它需要相当多的代码空间。当您删除最后一次使用它时,链接器将从最终的二进制文件中删除它,使它们更小。尝试改用 std::ofstream 。如果您只在输出文件中插入(例如)整数和字符串,那么只有处理整数和字符串的代码会被链接。

        场景 3 不太可能发生 - 并且可能表明 fprintf 在您使用它的任何地方都被内联

        希望对你有帮助

        【讨论】:

          【解决方案4】:

          第一个问题的答案取决于您使用的编译器;你只能通过检查你的编译器得到一个明确的答案。正如 GrahamS 所指出的,格式化程序的实现可能很复杂。

          尝试使用fputs 而不是fprintf 以避免使用格式化程序。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-11-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多