"The input/output library <stdio.h> shall not be used."

"The input/output library <stdio.h> shall not be used."

我正在阅读 JSF AV C++ Coding Standards 规则 22 说:

AV Rule 22 (MISRA Rule 124, Revised)

The input/output library <stdio.h> shall not be used.

有什么理由不使用<stdio.h>

我知道这些规则适用于 C++,我们可以使用 <iostream>...但是 <stdio.h> 有什么问题?

最明显的问题是 printfscanf 及其各种同类(例如 sprintf)缺乏类型安全。

除了类型安全问题外,scanfsprintf 系列还很难确保不会出现缓冲区溢出。可以做到,但并非微不足道,而且似乎只有极少数 C 程序员完全知道如何处理此类任务。

时不时会有这样的问题,关于为什么某些编码标准要求这样或那样。

正确答案应该始终是:无论该编码标准的作者意图如何。有时,编码标准会给出其规则的原因;这个显然没有。因此,我们只能猜测您问题的答案可能是什么。

因此我将添加我自己的猜测:

飞行器听起来真的很严肃,对正确性和稳健性要求极高。 printfscanf 等使编写代码变得容易,这些代码可以愉快地通过编译器但在 运行 时间创建未定义的行为。

那些 C 函数确实比普通的 C++ 流有一个优势:格式字符串使编写国际化代码变得更容易。考虑这个使用 std::ostream 快速制作的示例,用于某些 UI 组件,例如客户管理应用程序中的按钮标签:

os << "Update customer\n";

您可能想要动态添加客户的姓名:

os << "Update " << customer_name << "\n";

并且您可能想要动态插入 "Update" 的外语单词:

os << InternationalString(id_update) << " " << customer_name << "\n";

然而,这本质上是以英语为中心的。它将生成正确的英文字符串,如 "Update John" 或 "Update Joe",但例如德语失败,它有 不同的语法 并且需要像 "John aktualisieren" 或 "Joe aktualisieren" 这样的字符串,在这种情况下名称在动词前面。

这是格式字符串的亮点:

sprintf(s, FormatString(id_update_customer), customer_name);

根据应用程序的语言设置,这样的 FormatString 函数可能 return "Update %s""%s aktualisieren".

不过,这还是很危险的。事实上,如果我上面的示例中的 customer_namestd::string,那么您已经有未定义的行为了。

Boost.Format 这样的库试图将格式字符串的灵活性与流的类型安全性结合起来。

回到您的实际问题,我的猜测是国际字符串的灵活性在飞行器业务中并不是一个大问题。


如您所见,编码标准的各个方面都可以进行详尽的讨论。个别规则的原因可能并不总是那么明显,它们通常与编写标准的应用领域有关。

如果你不能问编码标准的作者,那么你的问题就真的没有答案。

此 JSF C++ 标准引用了 MISRA-C:1998,两者都与安全关键系统中 C 语言的使用有关。最新标准 MISRA-C:2012 已更新(提供更多原理和更好的示例),MISRA Rule 124 现在是 Rule 21.6 "The Standard Library input/output functions shall not be used"。

基本原理非常简单:流和文件 I/O 具有未指定、未定义和实现定义的相关行为。指南中还包括对描述这些不可预测行为的 C 标准(C90 和 C99)部分的引用。

如果实际实施定义明确或代码显示不重要,则可以(通过偏差过程)打破规则。