当前位置: 首页 > 知识库问答 >
问题:

为什么printf中与转换说明符不匹配的参数是未定义的行为?

王君墨
2023-03-14

在 C (n1570 7.21.6.1/10) 和 C(通过包含 C 标准库)中,向类型与其转换规范不匹配的 printf 提供参数是未定义的行为。一个简单的例子:

printf("%d", 1.9)

格式字符串指定 int,而参数是浮点类型。

这个问题的灵感来自一个用户的问题,他遇到了大量转换不匹配的遗留代码,这显然没有伤害,参见。理论和实践中的未定义行为。

最初,仅仅声明格式不匹配的UB似乎很激烈。很明显,输出可能是错误的,这取决于诸如完全不匹配、参数类型、结束方式、可能的堆栈布局和其他问题。正如一位评论员所指出的,这也延伸到了后来的(甚至是以前的?)论据。但这远远不是一般的UB。就我个人而言,除了预期的错误输出之外,我从未遇到过其他任何问题。

大胆猜测一下,我会排除对齐问题。我所能想象的是,提供一个让printf期待大数据和小实参的格式字符串可能会让< code>printf读取堆栈之外的数据,但是我缺乏对var args机制和具体printf实现细节的深入了解来验证这一点。

我快速查看了printf源代码,但对于普通读者来说,它们相当不透明。

因此,我的问题是:在printf中错误匹配转换说明符和参数会带来什么具体的危险?

共有3个答案

濮景龙
2023-03-14

举个例子:假设你的架构的过程调用标准说浮点参数在浮点寄存器中传递。但是< code>printf认为您传递的是一个整数,因为有< code>%d格式说明符。所以它期望在调用堆栈上有一个参数,但实际上并没有。现在任何事情都可能发生。

罗翰
2023-03-14

一些编译器可能以允许验证参数类型的方式实现变量格式参数;由于在不正确的使用上设置程序陷阱可能比输出看似有效但错误的信息要好,因此一些平台可能会选择这样做。

因为陷阱的行为超出了C标准的范围,所以任何可能被认为是陷阱的行为都被归类为调用未定义行为。

请注意,基于不正确的格式实现陷印的可能性意味着即使在预期类型和实际传递类型具有相同的表示形式的情况下,行为也被认为是未定义的,除了如果相同等级的有符号和无符号数字在两者共同的范围内(即,如果“long”包含23, 它可能以“%lX”输出,但不能使用“%X”输出,即使“整数”和“长”大小相同]。

另请注意,C89委员会通过法令引入了一条规则,该规则至今仍保留,该规则规定即使“int”和“long”具有相同的格式,代码:

long foo=23;
int *u = &foo;
(*u)++;

调用未定义的行为,因为它会导致以“long”类型编写的信息被读取为类型“int”(如果行为类型为“未签名 int”,则行为也将是未定义的)。由于“%X”格式说明符会导致数据被读取为类型“无符号int”,因此将数据作为类型“long”传递几乎肯定会导致数据存储在“long”的某个位置,但随后被读取为类型“无符号int”,这种行为几乎可能会违反上述规则。

钱经业
2023-03-14

printf只有在正确使用的情况下才能按照标准描述工作。如果您使用不正确,行为是未定义的。为什么标准要定义当您使用错误时会发生什么?

具体来说,在某些架构上,浮点参数在不同的寄存器中被传递给整数参数,因此在< code>printf内部,当它试图找到与格式说明符匹配的< code>int时,它会在相应的寄存器中找到垃圾。因为这些细节超出了标准的范围,所以除了说它是未定义的之外,没有办法处理这种不当行为。

例如,使用格式说明符"%p"但传递浮点类型可能意味着printf尝试从未设置为有效值的寄存器或堆栈位置读取指针,并且可能包含陷阱表示,这将导致程序中止。

 类似资料:
  • 有了,我可以使用表示

  • 在我正在读的一本书里,写着带有单个参数(没有转换说明符)的< code>printf被弃用。它建议替换为 与 或者 有人能告诉我为什么是错的吗?书上写着它包含漏洞,这些漏洞是什么?

  • 问题内容: 遇到一个有趣的面试问题: 尽管这在某些系统上可能运行良好,但至少我的系统出现了分段错误。对此行为的最佳解释是什么?上面的代码在C中。 以下是我的gcc信息: 问题答案: 首先,第一件事是:期望其%s参数使用有效的(即非NULL)指针,因此将NULL正式传递给它是不确定的。它可能会显示“(null)”,也可能会删除硬盘上的所有文件- 就ANSI而言,这是正确的行为(至少,这是Harbis

  • 由于ANSI C99,通过有或。但是bool是否也有格式说明符? 它将打印:

  • 假设我有以下功能: 我希望调用者指定要打印的浮点数,以及小数点后的位数。(由<代码>表示)。%d说明符) 然而,在编译此代码时,我收到两个警告: 和 我的问题是:如何将格式说明符(例如本例中的)注入现有格式说明符?

  • 您可以在David Vandevoorde和Nicolai Josuttis的《C++模板完整指南》一书的附录B中找到下面的文本。 null 几页后,这本书展示了以下示例和文本(重点是我的): 起初,表达式STR[5]似乎没有任何歧义。(1)处的下标运算符似乎是一个完美的匹配。但是,它并不十分完美,因为参数5的类型是int,而运算符期望的是无符号整数类型(size_t和STD::size_t通常具