How cout is more typesafe than printf()
Asked Answered
D

5

7

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.

Disaffection answered 22/7, 2013 at 13:54 Comment(4)
because we explicitly pass format string suppose if I misspelled %d with %f then -- undefined behavior.Evocative
possible duplicate of printf vs cout in C++Coniology
Since you mention type safe, see https://mcmap.net/q/686451/-printf-vs-std-cout-duplicate and #2017989 and ...Polyunsaturated
printf is a varadic function en.wikipedia.org/wiki/Variadic_function which are notoriously not type safe.Pushup
G
10

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.

Gracye answered 22/7, 2013 at 13:57 Comment(2)
GCC's format attribute can be used to apply the same checks to your own myprintf functionTinsmith
Your example will not cause buffer overflow!!! It will just print string address.Callipash
N
8

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.

Nilla answered 22/7, 2013 at 14:0 Comment(0)
I
3
  1. 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:

  2. 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.

Isogamete answered 22/7, 2013 at 14:5 Comment(0)
L
2

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.

Leifeste answered 22/7, 2013 at 15:15 Comment(0)
S
1

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.

Saleme answered 22/7, 2013 at 14:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.