【问题标题】:Alternatives to printf() for MISRA C : 2004 compliant codeMISRA C : 2004 兼容代码的 printf() 替代方案
【发布时间】:2021-03-22 06:21:44
【问题描述】:

首先,如果问题中有任何语法错误,我想道歉。

言归正传,我对使用 MISRA C 指南进行编码有点陌生,

遵循 MISRA C 2004 中的两条规则:

规则 16.1(必需):函数不应定义为可变数量的 论据。 规则 20.9(必需):输入/输出库 不得用于 生产代码。

这显然意味着我不能在生产代码中使用 printf 以使其符合 MISRA C,因为 printf<stdio.h> 的一部分并允许可变数量的参数,所以我开始了寻求找出我如何编写自己的 printf 声明,到目前为止,我无法找到任何解决此困境的方法,我们将不胜感激其他开发人员的帮助。

【问题讨论】:

  • 如果您编写了自己的“printf”,它最终也会成为一个可变参数函数。所以第一步是决定 what 你需要的 printf 部分不需要可变数量的参数。
  • 这里可能有些灵感:stackoverflow.com/questions/5528389/…
  • 咦,为什么要在主题中添加“关闭”?这不是关闭的工作方式。如果您对各种 PC 程序员的回答感到沮丧,您可以在问题中添加embedded 标签来吓跑他们。总体而言,准确了解您所针对的系统和硬件类型会很有帮助。
  • 另外,为什么您使用旧的 MISRA-C:2004 而不是最新的? (不过,在这种特定情况下,它们具有相同的要求。)
  • 好的,这是一个通用库。但是在这种情况下你需要 printf 做什么?只是为了测试? MISRA 允许您在调试版本中使用 stdio.h,但不能在生产中使用。或者,如果您需要某种方式的日志记录,您可以创建一个函数,将一个字符串丢给调用者,然后让调用者担心如何呈现它。

标签: c stdio misra


【解决方案1】:

到目前为止,我无法找到任何解决此困境的方法

您必须使用一次打印一个(可数)事物的函数。您可能想要实现的示例接口可能如下所示:

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

【讨论】:

  • 感谢您的回复!不幸的是,您提到的功能仅在基于 x86 的处理器上运行,我正在基于 x86 和 arm 架构创建一个库,充当不同类型处理器之间的中间件。因此,即使这是使用基于 x86 的处理器的解决方案,但它并不支持所有架构,无论如何,感谢您的回复!我想我会去记录偏差
  • 您可能想在问题中说明这一点。 GIGO,以及所有这些 ;-)
  • the functions that you mention run on x86 based processors this doesn't support every architecture 我不明白。当然,每个具有 C 编译器的体系结构都支持带有一个参数的函数。提到了哪些功能?这些print_int 和我在这里列出的其他是抽象函数,它们显示了您可能想要实现的示例可能接口。尽管如此,该实现还是可以移植的,只有 putchar 部分是特定于实现的。
  • @KamilCuk 是的,我也对这种反应感到困惑。我之前已经为 8 位和 32 位微控制器实现了这个精确的东西(实际上在我理解可变参数魔法实际上是如何工作之前)。它很笨重,但如果你必须避免使用 VAR ARGS,它可以工作。
【解决方案2】:

所以我开始探索如何编写自己的 printf 语句

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

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

编辑

由于您似乎在制作某种通用库,因此一种解决方案是简单地提供一个 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);

这将返回一个指向您的库分配的内部static 字符串的指针。这样做的缺点是它在多进程环境中不能很好地工作。

【讨论】:

    【解决方案3】:

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

    您还需要了解,不能盲目地遵循 MISRA 指南...然后您需要了解 MISRA 的这些好心人在实际指南之前提供了几章有用的材料。其中一部分是偏差程序。

    如果您可以证明您认为需要违反准则的原因,请使用指定的偏差程序。这需要您了解违规的性质,以及您将采取哪些措施来确保应用程序的完整性。

    如果您确实需要使用 printf() 并且可以证明这一点,请在有偏差的情况下使用它

    【讨论】:

      【解决方案4】:

      在 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!
      

      【讨论】:

      • 谢谢克里斯!不幸的是,我正在使用 MISRA C 指南为可以在不同架构的处理器上运行的库编写代码,所以即使这是另一种解决方案,它对我不起作用,无论如何我决定偏离那些 2我之前提到的规则。感谢您的宝贵指导!
      • 那么,如果它对你不起作用,你为什么接受它作为答案?
      • 这也与 MISRA-C 兼容相去甚远,因此没有帮助。你必须使用 stdint.h 类型,你必须封装内联 asm 等等。但我认为,在 x86 Linux 程序上有 MISRA-C 要求的情况很少见。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      • 2019-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-26
      相关资源
      最近更新 更多