I have read this at many places, but do not understand. Why it is said that cout is more type safe than printf(). Just because it does not required to write %d %c %f
or it has some deeper meaning.
Thanks in advance.
I have read this at many places, but do not understand. Why it is said that cout is more type safe than printf(). Just because it does not required to write %d %c %f
or it has some deeper meaning.
Thanks in advance.
This is why:
printf("%s\n", 42); // this will clobber the stream
This will cause a buffer overflow – the compiler cannot generally check that the format string in the first argument of printf
corresponds to the types of the subsequent arguments. It could do this in the above case – because the string is hard-coded – and some compilers do.1 But in general the format string may be determined at runtime so the compiler cannot check its correctness.
1 But these checks are special-cased to printf
. If you wrote your own myprintf
function with the same signature as printf
, there would be no way to check for type safety since the signature uses ellipsis ...
which elides all type information inside the function.
format
attribute can be used to apply the same checks to your own myprintf
function –
Tinsmith The printf
family functions are variadic functions, as all of them uses ellipsis ...
which means argument(s) of any type(s) can be passed to the function as far as ...
is concerned. There is no restriction by the compiler, as there is no requirement on the types of the arguments. The compiler cannot impose any type-safety rule as the ellipsis ...
allows ALL types. The function uses a format string to assume the argument type (even if there is a mismatch!!). The format string is read and interpreted at runtime, by which time the compiler cannot do anything if there is mismatch because the code is already compiled. So in this way, this is not type-safe. By type-safe, we usually mean that the compiler is able check type consistency of the programs, by imposing rules on the types of (unevaluated) expressions.
Note that if there is a mismatch (which the function cannot figure out!), the program enters into the undefined-behaviour zone, where the behavior of the program is not predictable and theoretically anything could happen.
You can extend the same logic to any variadic function functions such as scanf
family.
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.
Generally, compilers can't check arguments from printf, don't even argument count, neither check if they are suited format string. They are "optimized" to do that work, but is a special case, and may fail. Example:
printf("%s %d\n", 1, "two", 3);
This will compile (unless optimized compiler detects failure), and at runtime, printf will consider first argument (1) a string and second ("two") a integer. printf will not even notice that there is a third argument, neither will notice if there aren't enough arguments!
Using cout, compiler must choose specific operator<< for each variable you insert. Example:
cout<<1<<"two"<<3<<endl;
Compiler must change this in calls to corresponding ostream&operator<<(int)
and ostream&operator<<(const char*)
(and also ostream&operator<<(ios&(*)(ios&))
).
cout will also be faster, as there is no runtime interpretation of format string.
From the C++ FAQ :
[15.1] Why should I use
<iostream>
instead of the traditional<cstdio>
?[...]
More type-safe: With , the type of object being I/O'd is known statically by the compiler. In contrast, uses "%" fields to figure out the types dynamically.
[...]
For printf
, the compiler cannot check that the format script of the first argument corresponds to the types of the other arguments... In general it is done at runtime.
© 2022 - 2024 — McMap. All rights reserved.
%d
with%f
then -- undefined behavior. – Evocativeprintf
is avaradic function
en.wikipedia.org/wiki/Variadic_function which are notoriously not type safe. – Pushup