符合MISRA C:2004标准的printf()替代方案

4

我刚开始使用MISRA C指南进行编码。

以下是MISRA C 2004中的两个规则:

规则16.1 (必须满足):函数不应定义为可变数量的参数。 规则20.9 (必须满足):生产代码中不应使用输入/输出库<stdio.h>

这意味着我不能在生产代码中使用<stdio.h>中的printf,因为它允许一个可变数量的参数。因此,我开始寻找如何编写自己的printf语句。到目前为止,我无法找到任何解决方案。希望能得到其他开发者的帮助。


3
如果你自己编写 "printf" 函数,它最终也会成为一个可变参数函数。因此,第一步是确定printf中不需要变量数量的哪些部分或部件是必需的。 - dxiv
1
这里可能会有一些灵感:https://dev59.com/vG035IYBdhLWcg3wQtog - nielsen
4
为什么你在话题中添加了“closed”?这不是关闭话题的方式。如果你对各种PC程序员的回答感到沮丧,你可以将[标签:embedded]标签添加到问题中来避开他们。总体而言,了解你所针对的系统和硬件类型将会很有帮助。 - Lundin
1
另外,您使用旧版的MISRA-C:2004而不是最新版的原因是什么?(尽管在这种特定情况下它们具有相同的要求。) - Lundin
1
好的,所以这是一个通用库。但在这种情况下,你需要printf吗?只是为了测试吗?MISRA允许您在调试版本中使用stdio.h,但不允许在生产中使用。或者如果您需要某种记录方式,您可以创建一个函数将字符串传递给调用者,然后让调用者担心如何呈现它。 - Lundin
显示剩余3条评论
4个回答

4

迄今为止,我无法找到解决这个困境的任何方案。

你需要使用每次仅打印一件事物的函数。您可能想要实现的示例接口可能如下所示:

print_string("Hello");
print_int(5);
print_char('\n');

1
谢谢您的回复!不幸的是,您提到的函数仅在基于x86的处理器上运行,我正在创建一个库,作为不同类型处理器之间的中间件,基于x86和arm架构。因此,即使这是适用于使用基于x86的处理器的解决方案,但它并不支持每种架构。无论如何,感谢您的回复!我想我会选择记录偏差。 - Wait What
1
你可能想在问题中表明这一点。垃圾输入,等等;-) - Mawg says reinstate Monica
3
你提到的函数在基于x86的处理器上运行。这并不支持每个架构。我不明白。肯定每个具有C编译器的架构都支持带一个参数的函数。哪些函数被提到了?我在这里列出的print_int和其他函数是抽象函数,展示了可能要实现的接口示例。不过,实现可以是可移植的,只有putchar部分是特定于实现的。 - KamilCuk
@KamilCuk 是的,我也对那个回复感到困惑。我以前已经为8位和32位微控制器实现了这个确切的东西(实际上是在我理解可变参数魔术如何工作之前)。它很笨重,但如果你必须避免VAR ARGS,它可以工作。 - Kurt E. Clothier

1

所以我开始寻找如何编写自己的printf语句

大多数MISRA-C系统都是嵌入式系统,其中printf只是一个笨重的UART库包装器。通常的解决方案是开发自己的日志/消息工具。不一定是基于UART的,也可以是其他串行总线,或者只是8个并行数据或一些LCD / 7-seg…这完全取决于您需要显示的内容以及是否打算将其作为生产代码的一部分。

因此,如何做到这一点高度依赖于项目,并且通常更多地是系统设计和电子问题,而不是编程问题。

编辑

由于您似乎正在制作某种通用库,因此一种解决方案是简单地提供API,该API返回字符串给调用者,然后让调用者担心如何呈现它们。这使得您的lib符合MISRA-C标准,同时允许调用者以他们可用的任何应用程序特定方式打印字符串。例如:

void lib_getmsg (char* msg, size_t bufsize);

这里的“lib”是你的库的一些前缀。字符串分配留给调用者处理。或者可以选择传统的方式:

lib_result_t  lib_dosomething (void);

// Returns LIB_OK if went OK, returns LIB_ERR in case of errors.
// To get more information, call lib_get_lastmsg.

const char* lib_get_lastmsg (void);

这将返回一个由你的库分配的内部静态字符串的指针。不足之处在于它在多进程环境中表现不佳。

0

您需要理解MISRA C准则的基本原理,了解它们所使用的上下文以及您自己代码的情况。

您还需要明白,MISRA准则不应该被盲目地遵循,也不应该只是简单地打勾... 您需要欣赏MISRA提供的实际准则之前的几章有用的材料。其中一部分是偏离程序。

如果您可以证明为什么需要违反某个准则,则使用指定的偏离程序。这要求您理解违规的性质以及您将采取什么措施来确保应用程序的完整性。

如果您真正需要使用printf()并且可以证明其必要性,请使用偏离程序。


-1

在Linux上,运行在现代的x86_64处理器上:

int main()
{
    char *s = "Hello, World!\n";
    long l = 14;
    long fd = 1;
    long syscall = 1;
    long ret = 0;
    __asm__("syscall"
            : "=a"(ret)
            : "a"(syscall),
              "D"(fd),
              "S"(s),
              "d"(l));
    return 0;
}

输出:

Hello, World!

2
谢谢你,克里斯!不幸的是,我正在使用MISRA C指南编写一种可以在不同架构的处理器上运行的库的代码,因此即使这是另一个解决方案,它也不适用于我。无论如何,我已经决定偏离我之前提到的那两个规则。感谢您宝贵的指导! - Wait What
2
那么,如果它对你没有用,为什么你要接受它作为答案呢? - Mawg says reinstate Monica
3
这个程序远远不符合MISRA-C标准,因此并没有什么帮助。您需要使用stdint.h类型,必须封装inline asm等等。但我认为,在x86 Linux程序中要求MISRA-C标准是相当罕见的。 - Lundin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接