210

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.

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
Chunky Chunk
  • 16,553
  • 15
  • 84
  • 162
  • 8
    Just to let you know, a lot of people [criticize](http://stackoverflow.com/questions/5250596/how-should-i-undo-damage-caused-by-reading-c-primer-plus) 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 Jun 02 '12 at 21:27
  • 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 :) – Chunky Chunk Jun 02 '12 at 21:30
  • Using the highest compiler warning would answer your question - **when** compiling with gcc. How MSVC handles this - I don't know. – Peter VARGA Apr 09 '18 at 20:31

9 Answers9

315

C++23 Update

We now finally have std::print as a way to use std::format for output directly:

#include <print>
#include <string>

int main() {
    // ...
    std::print("Follow this command: {}", myString);
    // ...
}

This combines the best of both approaches.

Original Answer

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
  • 60,560
  • 13
  • 143
  • 205
  • @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 Jan 09 '13 at 01:11
  • Update: std::format is included in C++20, though it's not included in clang at the moment (not sure about gcc) – Ovinus Real Apr 14 '21 at 20:17
  • 1
    @OvinusReal, It is, but there's no new function (yet) to print to `stdout`. `std::format` alone doesn't actually help here since there's no real formatting going on (`cout << "..." << s;` vs. `cout << format("... {}", s);`). – chris Apr 14 '21 at 20:36
42

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

thanks

Karsten Koop
  • 2,475
  • 1
  • 18
  • 23
Alessandro Pezzato
  • 8,603
  • 5
  • 45
  • 63
40

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
  • 476,176
  • 80
  • 629
  • 1,111
  • when i try to compile `cout << myString << endl;` i receive the following error: `Error 1 error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'std::string' (or there is no acceptable conversion)` – Chunky Chunk Jun 02 '12 at 21:39
  • 11
    @TheDarkIn1978: You probably forgot to `#include `. VC++ has some oddities in its headers that will let you define a string, but not send it to `cout`, without including the `` header. – Jerry Coffin Jun 02 '12 at 21:39
  • 43
    @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 Feb 05 '13 at 16:00
  • 9
    @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 Feb 05 '13 at 16:55
  • 1
    What is the counter part of printf in c++ when those parameters come in action? ( i forgot the name, i mean those % some letters stuff!) – Hossein Jan 17 '14 at 03:37
  • 2
    @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 Jan 17 '14 at 04:07
  • 10
    OK, 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 Aug 31 '14 at 15:45
  • 5
    printf is thread-safe (cout is not) and a lot of formatting performs better using printf than using streams (cout, stringstream, etc.). – ebasconp May 04 '15 at 17:58
  • 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 May 04 '15 at 19:58
  • 5
    A 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 Jun 29 '15 at 07:36
  • 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 Jun 29 '15 at 20:11
  • 3
    @JerryCoffin Someone went to the trouble of writing this: https://msdn.microsoft.com/en-us/magazine/dn913181.aspx Clearly it has its fans :-) – ForeverLearning Jul 13 '15 at 15:29
  • 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 Jul 13 '15 at 15:51
  • 2
    @kuroineko I agree 100% regarding your note _Typical..._ In my eyes `cout` is the worst _feature_ C++ offers. – Peter VARGA Apr 09 '18 at 20:34
  • @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 Apr 10 '18 at 00:53
  • 2
    No, 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 << "Hallo"` or a number. But do it with 30 arguments [as I spoke for a log]... – Peter VARGA Apr 10 '18 at 07:23
  • 1
    @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 Apr 10 '18 at 13:27
  • _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. – Peter VARGA Apr 10 '18 at 13:30
  • @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 Apr 10 '18 at 13:36
  • 1
    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 P Jun 21 '18 at 03:15
  • 6
    The day you'll have to translate all the output of a large software for i18n, you'll really really hate those who taught you to use cout. cout is dumb, not stateless (provided you use some library that's modifying the cout state, you'll end up with code like this "cout << 64" outputting "40". Code like this `cout << "The" << color << "house"` is untranslatable and so on. printf does not have any of those design defects. – xryl669 Mar 15 '20 at 13:10
  • @JerryCoffin Even while doing all the voodoo in the linked question about speed of cout vs printf, I found streams 40% slower than fprintf even if you call fprintf the number of times you use `<<`. I suggest you do a benchmark and share the code. I can be found in https://chat.stackexchange.com/rooms/38/ask-different-chat –  Jun 27 '20 at 20:53
15

Use std::printf and c_str() example:

std::printf("Follow this command: %s", myString.c_str());
Vertexwahn
  • 7,709
  • 6
  • 64
  • 90
Adel Ben Hamadi
  • 720
  • 10
  • 13
  • Is `c_str()` of a temporary object (value) valid by the time it is fed to the encompassing function `std::printf()`? – Sohail Si May 16 '23 at 09:49
  • Answer to my question: https://stackoverflow.com/questions/10006891/stdstringc-str-and-temporaries – Sohail Si May 16 '23 at 09:59
  • Quote from `james-kanze` : "The string object you're concerned about is a temporary. It will be destructed at the end of the full expression, not before and not after." from https://stackoverflow.com/users/649665/james-kanze 's answer – Sohail Si May 16 '23 at 10:01
  • 1
    @SohailSi intresting question and constructive answer https://stackoverflow.com/questions/10006891/stdstringc-str-and-temporaries – Adel Ben Hamadi May 19 '23 at 19:42
3

You can use snprinft to determine the number of characters needed and allocate a buffer of the right size.

int length = std::snprintf(nullptr, 0, "There can only be %i\n", 1 );
char* str = new char[length+1]; // one more character for null terminator
std::snprintf( str, length + 1, "There can only be %i\n", 1 );
std::string cppstr( str );
delete[] str;

This is a minor adaption of an example on cppreference.com

shuhalo
  • 5,732
  • 12
  • 43
  • 60
2

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
  • 87
  • 8
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
  • 349
  • 2
  • 10
0

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
  • 49
  • 1
  • 6
0

Here’s a generic way of doing it.

#include <string>
#include <stdio.h>

auto print_helper(auto const & t){
    return t;
}
auto print_helper(std::string const & s){
    return s.c_str();
}
std::string four(){
    return "four";
}
template<class ... Args>
void print(char const * fmt, Args&& ...args){
    printf(fmt, print_helper(args) ...);
}
int main(){
    std::string one {"one"};
    char const * three = "three";
    print("%c %d %s %s, %s five", 'c', 3+4, one + " two", three, four());
}
QuentinUK
  • 2,997
  • 21
  • 20