C++ printf with std::string?

125

24

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;
}

enter image description here

Each time the program runs, myString prints a seemingly random string of 3 characters, such as in the output above.

TheDarkIn1978

Posted 2012-06-02T21:07:06.060

Reputation: 9 635

8

Just to let you know, a lot of people criticize that book. Which I can understand, because there isn't much about object-oriented programming, but I don't think it is as bad as people claim.

– Jesse Good – 2012-06-02T21:27:24.000

ouf! well, it's good to keep this in mind while i make my way thru the book. I'm sure it won't be the only C++ book i'll read over the course of the next year or so, so i hope it doesn't do too much damange :) – TheDarkIn1978 – 2012-06-02T21:30:20.760

Using the highest compiler warning would answer your question - when compiling with gcc. How MSVC handles this - I don't know. – Al Bundy – 2018-04-09T20:31:17.737

Answers

189

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.

chris

Posted 2012-06-02T21:07:06.060

Reputation: 45 383

2no mention of using cout for the string as well? – Mooing Duck – 2013-01-09T01:08:54.840

@MooingDuck, Good point. It's in Jerry's answer, but being the accepted answer, this is what people see, and they might leave before seeing the others. I've added that option in so as to be the first solution seen, and the recommended one. – chris – 2013-01-09T01:11:31.057

37

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:

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

  4. 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).

Jerry Coffin

Posted 2012-06-02T21:07:06.060

Reputation: 380 820

when i try to compile cout &lt;&lt; myString &lt;&lt; endl; i receive the following error: Error 1 error C2679: binary '&lt;&lt;' : no operator found which takes a right-hand operand of type 'std::string' (or there is no acceptable conversion) – TheDarkIn1978 – 2012-06-02T21:39:03.940

9@TheDarkIn1978: You probably forgot to #include &lt;string&gt;. VC++ has some oddities in its headers that will let you define a string, but not send it to cout, without including the &lt;string&gt; header. – Jerry Coffin – 2012-06-02T21:39:38.707

that i did forget (actually didn't know about)! thanks :) – TheDarkIn1978 – 2012-06-02T21:41:12.847

21@Jerry: Just want to point out that using printf is MUCH faster than using cout when dealing with large data. Thus, please dont say that it is useless :D – Programmer – 2013-02-05T16:00:14.693

6

@Programmer: see http://stackoverflow.com/questions/12044357/printf-more-than-5-times-faster-than-stdcout. Summary: most times that cout is slower, it's because you've used std::endl where you shouldn't.

– Jerry Coffin – 2013-02-05T16:55:10.137

1What is the counter part of printf in c++ when those parameters come in action? ( i forgot the name, i mean those % some letters stuff!) – Breeze – 2014-01-17T03:37:35.457

1@Hossein: As far as I know the only functions in C++ that take printf-style format strings are the printf family that C++ "inherited" from C. – Jerry Coffin – 2014-01-17T04:07:53.953

19Typical C++ expert arrogance. If printf does exist, why not use it? – kuroi neko – 2014-08-31T14:32:15.610

1@kuroineko: I've edited the answer to explain the reasoning. – Jerry Coffin – 2014-08-31T15:22:35.200

6OK, sorry for the snappy comment. Still, printf is pretty handy for debugging and the streams, though vastly more powerful, have the drawback that the code does not give any idea of the actual output. For formatted output, printf is still a viable alternative, and it's a shame both systems can't cooperate better. Just my opinion, of course. – kuroi neko – 2014-08-31T15:45:07.117

1printf is thread-safe (cout is not) and a lot of formatting performs better using printf than using streams (cout, stringstream, etc.). – ebasconp – 2015-05-04T17:58:05.650

1@oopscene: POSIX requires printf to be thread-safe, but not all systems completely conform with POSIX (some, of course, don't even try). It's not clear enough what you mean by "performs better" to comment meaningfully on that. In the end, it comes down to one thing though: yes, there are at least cases where there's at least some reasonable argument to be made in favor of using printf--but such cases are a lot less common than some people think, and the case at hand isn't even close. – Jerry Coffin – 2015-05-04T19:58:31.660

2A major drawback of cout compared to printf is that cout doesn't (and printf does) offer formatting based on a format string. Such formatting is essential for internationalization of statements that print a sentence involving more than one value, because the order and placement of the values in the sentence may need to vary based on the language of the human reader of the printed sentence. Internationalization involves more than this, but with printf and a message catalog you get something useful a lot more quickly than with cout. – Louis Strous – 2015-06-29T07:36:14.250

1@LouisStrous: At one time I'd have agreed (and years ago posted similar statements). This allows you to do something, but do it so poorly that you're really better off not having done it at all, lest somebody mistake it for a finished/usable port. It leads to what think of as the "MS-DOS syndrome"--so poor it's almost unusable, but enough to convince many that it's good enough, so they don't try to improve it. – Jerry Coffin – 2015-06-29T20:11:48.930

1

@JerryCoffin Someone went to the trouble of writing this: https://msdn.microsoft.com/en-us/magazine/dn913181.aspx

Clearly it has its fans :-)

– ForeverLearning – 2015-07-13T15:29:30.270

1@Dilip: It does--and in a way, I'm even among them. I certainly don't like the verbosity of iostreams when you need to format output precisely at all (for only one obvious example).Nonetheless, if printf is the answer, somebody asked a lousy question. – Jerry Coffin – 2015-07-13T15:51:51.220

1@kuroineko I agree 100% regarding your note Typical... In my eyes cout is the worst feature C++ offers. – Al Bundy – 2018-04-09T20:34:38.210

@AlBundy: I agree that cout (well, iostreams in general) have a lot of problems. Even so, they're better than printf in a whole host of ways. – Jerry Coffin – 2018-04-10T00:53:31.303

1No, when working with gcc with all warning switches enabled. There is nothing more safe and especially more convenient: 100% typesafe, checking argument count. Formatting with cout is a horror - especially for long log files entries. I am always laughing when the C++ gurus come up with cout &lt;&lt; "Hallo" or a number. But do it with 30 arguments [as I spoke for a log]... – Al Bundy – 2018-04-10T07:23:43.933

@AlBundy: No, it's not even close to 100% type safe. The type checking only works when your format string is a string literal. If you're even trying to do the job professionally, that basically never happens--format strings are always loaded from an external file to allow for localization. In the process, you lose all type checking. – Jerry Coffin – 2018-04-10T13:27:35.263

The type checking only works when your format string is a string literal Yes, and this is what I am dealing with. Different situation -> another solution. gcc warns me already while the compilation. When I just think about the solution with cout and the rudimentary methods I get sick. – Al Bundy – 2018-04-10T13:30:47.427

@AlBundy: I'd suggest that you ask a question about how to do what you're trying to accomplish. I suppose it comes down to a question of what you're willing to put up with--as I already said (and I wasn't kidding) cout does suck. On the other hand, printf's complete lack of type checking goes way past sucking and well into unacceptable, bordering on criminal. – Jerry Coffin – 2018-04-10T13:36:00.173

Contrary to the suggestion, in my experience large complex C++ projects do not use or even outright forbid c++ streams if possible and prefer to use printf. This suggestion does make sense for beginners though – Pavel – 2018-06-21T03:15:18.883

23

use myString.c_str() if you want a c-like string (const char*) to use with printf

Alessandro Pezzato

Posted 2012-06-02T21:07:06.060

Reputation: 5 787

6

Use std::printf and c_str() example:

std::printf("Follow this command: %s", myString.c_str());

Adel Ben Hamadi

Posted 2012-06-02T21:07:06.060

Reputation: 171

2

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.

howard howard

Posted 2012-06-02T21:07:06.060

Reputation: 35

1

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.

MMacD

Posted 2012-06-02T21:07:06.060

Reputation: 274

1

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.

Hyena

Posted 2012-06-02T21:07:06.060

Reputation: 16