My understanding is that string
is a member of the std
namespace, so why does the following occur?
#include <iostream>
int main()
{
using namespace std;
string myString = \"Press ENTER to quit program!\";
cout << \"Come up and C++ me some time.\" << endl;
printf(\"Follow this command: %s\", myString);
cin.get();
return 0;
}
Each time the program runs, myString
prints a seemingly random string of 3 characters, such as in the output above.
It\'s compiling because printf
isn\'t type safe, since it uses variable arguments in the C sense1. printf
has no option for std::string
, only a C-style string. Using something else in place of what it expects definitely won\'t give you the results you want. It\'s actually undefined behaviour, so anything at all could happen.
The easiest way to fix this, since you\'re using C++, is printing it normally with std::cout
, since std::string
supports that through operator overloading:
std::cout << \"Follow this command: \" << myString;
If, for some reason, you need to extract the C-style string, you can use the c_str()
method of std::string
to get a const char *
that is null-terminated. Using your example:
#include <iostream>
#include <string>
#include <stdio.h>
int main()
{
using namespace std;
string myString = \"Press ENTER to quit program!\";
cout << \"Come up and C++ me some time.\" << endl;
printf(\"Follow this command: %s\", myString.c_str()); //note the use of c_str
cin.get();
return 0;
}
If you want a function that is like printf
, but type safe, look into variadic templates (C++11, supported on all major compilers as of MSVC12). You can find an example of one here. There\'s nothing I know of implemented like that in the standard library, but there might be in Boost, specifically boost::format
.
[1]: This means that you can pass any number of arguments, but the function relies on you to tell it the number and types of those arguments. In the case of printf
, that means a string with encoded type information like %d
meaning int
. If you lie about the type or number, the function has no standard way of knowing, although some compilers have the ability to check and give warnings when you lie.
Please don\'t use printf(\"%s\", your_string.c_str());
Use cout << your_string;
instead. Short, simple and typesafe. In fact, when you\'re writing C++, you generally want to avoid printf
entirely -- it\'s a leftover from C that\'s rarely needed or useful in C++.
As to why you should use cout
instead of printf
, the reasons are numerous. Here\'s a sampling of a few of the most obvious:
- As the question shows,
printf
isn\'t type-safe. If the type you pass differs from that given in the conversion specifier, printf
will try to use whatever it finds on the stack as if it were the specified type, giving undefined behavior. Some compilers can warn about this under some circumstances, but some compilers can\'t/won\'t at all, and none can under all circumstances.
printf
isn\'t extensible. You can only pass primitive types to it. The set of conversion specifiers it understands is hard-coded in its implementation, and there\'s no way for you to add more/others. Most well-written C++ should use these types primarily to implement types oriented toward the problem being solved.
It makes decent formatting much more difficult. For an obvious example, when you\'re printing numbers for people to read, you typically want to insert thousands separators every few digits. The exact number of digits and the characters used as separators varies, but cout
has that covered as well. For example:
std::locale loc(\"\");
std::cout.imbue(loc);
std::cout << 123456.78;
The nameless locale (the \"\") picks a locale based on the user\'s configuration. Therefore, on my machine (configured for US English) this prints out as 123,456.78
. For somebody who has their computer configured for (say) Germany, it would print out something like 123.456,78
. For somebody with it configured for India, it would print out as 1,23,456.78
(and of course there are many others). With printf
I get exactly one result: 123456.78
. It is consistent, but it\'s consistently wrong for everybody everywhere. Essentially the only way to work around it is to do the formatting separately, then pass the result as a string to printf
, because printf
itself simply will not do the job correctly.
- Although they\'re quite compact,
printf
format strings can be quite unreadable. Even among C programmers who use printf
virtually every day, I\'d guess at least 99% would need to look things up to be sure what the #
in %#x
means, and how that differs from what the #
in %#f
means (and yes, they mean entirely different things).
use myString.c_str() if you want a c-like string (const char*) to use with printf
Use std::printf and c_str()
example:
std::printf(\"Follow this command: %s\", myString.c_str());
Printf is actually pretty good to use if size matters. Meaning if you are running a program where memory is an issue, then printf is actually a very good and under rater solution. Cout essentially shifts bits over to make room for the string, while printf just takes in some sort of parameters and prints it to the screen. If you were to compile a simple hello world program, printf would be able to compile it in less than 60, 000 bits as opposed to cout, it would take over 1 million bits to compile.
For your situation, id suggest using cout simply because it is much more convenient to use. Although, I would argue that printf is something good to know.
The main reason is probably that a C++ string is a struct that includes a current-length value, not just the address of a sequence of chars terminated by a 0 byte. Printf and its relatives expect to find such a sequence, not a struct, and therefore get confused by C++ strings.
Speaking for myself, I believe that printf has a place that can\'t easily be filled by C++ syntactic features, just as table structures in html have a place that can\'t easily be filled by divs. As Dykstra wrote later about the goto, he didn\'t intend to start a religion and was really only arguing against using it as a kludge to make up for poorly-designed code.
It would be quite nice if the GNU project would add the printf family to their g++ extensions.
printf
accepts a variable number of arguments. Those can only have Plain Old Data (POD) types. Code that passes anything other than POD to printf
only compiles because the compiler assumes you got your format right. %s
means that the respective argument is supposed to be a pointer to a char
. In your case it is an std::string
not const char*
. printf
does not know it because the argument type goes lost and is supposed to be restored from the format parameter. When turning that std::string
argument into const char*
the resulting pointer will point to some irrelevant region of memory instead of your desired C string. For that reason your code prints out gibberish.
While printf
is an excellent choice for printing out formatted text, (especially if you intend to have padding), it can be dangerous if you haven\'t enabled compiler warnings. Always enable warnings because then mistakes like this are easily avoidable. There is no reason to use the clumsy std::cout
mechanism if the printf
family can do the same task in a much faster and prettier way. Just make sure you have enabled all warnings (-Wall -Wextra
) and you will be good. In case you use your own custom printf
implementation you should declare it with the __attribute__
mechanism that enables the compiler to check the format string against the parameters provided.