%d %c %f
或者有更深层的含义吗?提前致谢。
这就是原因:
printf("%s\n", 42); // this will clobber the stream
这将导致缓冲区溢出 - 编译器通常无法检查printf
函数的第一个参数中的格式字符串是否与后续参数的类型相对应。在上述情况下,它可能执行此操作 - 因为字符串是硬编码的 - 有些编译器确实可以这样做。1 但通常情况下,格式字符串可能会在运行时确定,因此编译器无法检查其正确性。
1 但这些检查只针对printf
函数。如果您自己编写具有与printf
相同签名的myprintf
函数,则没有办法检查类型安全性,因为该签名使用省略号...
,其中包含函数内部所有类型信息。
format
属性可用于将相同的检查应用于您自己的myprintf
函数。 - Jonathan Wakelyprintf
系列函数是可变参数函数,它们都使用省略号...
,表示可以将任何类型的参数传递给该函数。编译器没有任何限制,对参数的类型也没有要求,因此编译器不能强制执行任何类型安全规则,因为省略号...
允许所有类型。函数使用一个格式字符串来假设参数类型(即使存在不匹配!)。当运行时读取和解释格式字符串时,编译器已经无法进行任何操作,因为代码已经编译。因此,在这种情况下,它不是类型安全的。 通常所谓的类型安全是指编译器能够通过对(未计算的)表达式类型施加规则来检查程序的类型一致性。
请注意,如果存在不匹配(函数无法判断!),程序就会进入未定义行为区域,程序的行为是不可预测的,理论上可以发生任何事情。scanf
系列函数。the type system guarantees correctness with std::ostream
but not printf
.
Konrad's answer is one example, but something like
printf("%ld\n", 7);
is also broken (assuming long
and int
are different sizes on your system). It may even work with one build target and fail with another. Trying to print typedefs like size_t
has the same problem.
This is (somewhat) solved with the the diagnostic provided by some compilers, but that doesn't help with the second sense:
both type systems (the run-time type system used in the format string, and the compile-time system used in your code) cannot be kept in sync automatically. For example, printf
interacts badly with templates:
template <typename T> void print(T t) { printf("%d\n",t); }
you can't make this correct for all types T
- the best you can do is static_cast<int>(t)
so it will fail to compile if T is not convertible to int. Compare
template <typename T> void print(std::ostream& os, T t) { os << t << '\n'; }
which selects the correct overload of operator<<
for any T
that has one.
通常,编译器无法检查printf的参数,甚至不会检查参数计数,也不会检查它们是否适合格式字符串。它们被“优化”来执行该工作,但这是一种特殊情况,可能会失败。 例如:
printf("%s %d\n", 1, "two", 3);
这段代码在编译时会通过(除非优化编译器检测到错误),运行时,printf函数将第一个参数(1)视为字符串,第二个参数(“two”)视为整数。printf甚至不会注意到有第三个参数,也不会注意到参数不足的情况!
使用cout时,编译器必须为每个插入的变量选择特定的operator<<操作符。 例如:
cout<<1<<"two"<<3<<endl;
ostream&operator<<(int)
、ostream&operator<<(const char*)
(以及 ostream&operator<<(ios&(*)(ios&))
)时进行更改。
由于没有格式字符串的运行时解释,cout 也会更快。
来自C++ FAQ:
[15.1] 为什么我应该使用
<iostream>
而不是传统的<cstdio>
?[...]
更加类型安全:使用
<iostream>
,编译器可以静态地知道被 I/O 的对象的类型。相比之下,<cstdio>
使用“%”字段动态确定类型。[...]
对于 printf
,编译器无法检查第一个参数的格式脚本是否与其他参数的类型相对应...... 通常在运行时完成。
printf
是一个可变参数函数https://en.wikipedia.org/wiki/Variadic_function,它以不安全的方式处理类型。 - Shafik Yaghmour