Can you find the bug in this program?

#include <stddef.h>
#include <stdlib.h>
#include <iostream>

int main(int argc,char *argv[])
{
    int val1 = 493,val2 = 634;

    if (argc > 1 && !strcmp(argv[1],"octal"))
        std::cout << "Octal: val1 = " << std::oct << val1 << std::endl;
    else
        std::cout << "Decimal: val1 = " << val1 << std::endl;
    std::cout << "val2 = " << val2 << std::endl;
    return EXIT_SUCCESS;
}

Hint: run the program twice, once with no parameters and once with the parameter "octal", and look at the second line of output.

With no parameters, the program prints:

$ streams_octal
Decimal: val1 = 493
val2 = 634

When run with the parameter "octal", the program prints:

$ streams_octal octal
Octal: val1 = 755
val2 = 1172

The second value is the same in both runs, and the print command is the same in both runs. Yet the program prints different results each time.

The problem is that streams such as std::cout are global objects, and std:oct, std::hex, and std::dec modify the state of those global objects. The modification then persists beyond the << or >> operators that use the streams. Thus, any code that you call anywhere in your program can scramble number interpretation or formatting.

The safest thing to do is reset the state of the input or output stream every time you read or write data. Until you do, your program is vulnerable to bugs that will be difficult to track down.

Bjarne Stroustrup mentions this problem almost in passing in "The C++ Programming Language" (3rd edition, section 21.4.6.3, "User-Defined Manipulators") but a complete solution is left as an exercise for the reader (literally!), implying that it is only a convenience.

The syntax for stream input and output also makes it harder for a programmer to visualize the result. The repeated << operators in output statements are also much more verbose than an equivalent printf() statement. In combination these problems make output-intensive C++ code harder to develop and maintain than code which uses printf() and its variants.

Stream output syntax also prevents message substitution for localization as described in my article "An Error Manager with Message Text Replacement", printed in the C/C++ Users Journal, April 1995, pp. 23-39. Runtime format string replacement allows easy localization without rewriting, recompiling, or relinking the original code.

printf() and its variants have their own problems, of course, but global state is not one of them. Static compile-time checking can detect many type problems in printf() calls, and a simple utility program can extend this to format string replacement, but nothing can detect number format problems in streams code for you.

There might be more problems with C++ streams, but I consider these two fatal and so I never explored the library further. The use of streams as currently recommended is unsafe. It's long past time to abandon "sticky" states in I/O packages.

Contents of this Web site Copyright © 2011-2020 by David C. Chapman. All Rights Reserved. Contact me

Joomla Templates: from JoomlaShack