cout如何比printf()更安全

How cout is more typesafe than printf()

本文关键字:安全 printf 何比 cout      更新时间:2023-10-16

我在很多地方读过这篇文章,但不理解。为什么说cout比printf()更安全。只是因为它不需要写%d %c %f,或者它有一些更深的含义。

提前谢谢。

这就是为什么:

printf("%sn", 42); // this will clobber the stream

这将导致缓冲区溢出——编译器通常无法检查printf的第一个参数中的格式字符串是否与后续参数的类型相对应。在上述情况下,它可以这样做,因为字符串是硬编码的,一些编译器也会这样做。1但通常情况下,格式字符串可能在运行时确定,因此编译器无法检查其正确性。


1但是这些检查对于printf是特殊情况。如果您使用与printf相同的签名编写自己的myprintf函数,则将无法检查类型安全性,因为签名使用省略号...,该省略号会删除函数内的所有类型信息。

printf族函数是变元函数,因为它们都使用省略号...,这意味着就...而言,任何类型的参数都可以传递给函数。编译器没有限制,因为参数的类型没有要求。编译器不能强制执行任何类型安全规则,因为省略号...允许ALL类型。函数使用格式字符串来假设参数类型(即使存在不匹配!!)。格式字符串在运行时读取和解释,此时,如果由于代码已编译而出现不匹配,编译器将无法执行任何操作。因此,在这种情况下,这不是类型安全的所谓类型安全,通常是指编译器能够通过对(未赋值的)表达式的类型强加规则来检查程序的类型一致性

请注意,如果存在不匹配(函数无法解决!),程序将进入未定义的行为区域,在该区域,程序的行为是不可预测的,理论上任何事情都可能发生。

您可以将相同的逻辑扩展到任何可变函数函数,如scanf族。

  1. 类型系统用CCD_ 12而不是CCD_。康拉德的回答是一个例子,但有点像

    printf("%ldn", 7);
    

    也已损坏(假设系统中的longint大小不同)。它甚至可能与一个构建目标一起工作,而与另一个目标一起失败。尝试打印像size_t这样的typedef也有同样的问题。

    一些编译器提供的诊断程序(在某种程度上)解决了这个问题,但这对第二种感觉没有帮助:

  2. 两个类型系统(格式化字符串中使用的运行时类型系统和代码中使用的编译时系统)都不能自动保持同步。例如,printf与模板的交互很差:

    template <typename T> void print(T t) { printf("%dn",t); }
    

    你不能对所有类型的T都这样做-你能做的最好的是static_cast<int>(t),所以如果t不能转换为int,它将无法编译。比较

    template <typename T> void print(std::ostream& os, T t) { os << t << 'n'; }
    

    其为具有CCD_ 21的任何CCD_。

通常,编译器不能检查printf中的参数,甚至不能检查参数计数,也不能检查它们是否适合字符串格式。它们被"优化"以完成这项工作,但这是一种特殊情况,可能会失败。示例:

printf("%s %dn", 1, "two", 3);

这将进行编译(除非优化的编译器检测到失败),并且在运行时,printf将第一个参数(1)视为字符串,第二个参数("2")视为整数。printf甚至不会注意到有第三个参数,如果没有足够的参数,也不会注意到!

使用cout,编译器必须选择特定的运算符<lt;对于您插入的每个变量。示例:

cout<<1<<"two"<<3<<endl;

编译器必须在调用相应的ostream&operator<<(int)ostream&operator<<(const char*)(以及ostream&operator<<(ios&(*)(ios&)))时更改此项。

cout也会更快,因为没有对格式字符串的运行时解释。

来自C++常见问题解答:

[15.1]为什么我应该使用<iostream>而不是传统的<cstdio>

[…]

更安全的类型:通过,编译器静态地知道要I/O的对象的类型。相反,使用"%"字段来动态地计算类型。

[…]

对于printf,编译器无法检查第一个参数的格式脚本是否与其他参数的类型相对应一般来说,它是在运行时完成的