1

I'm trying to convert a char array to an std::string, but I only get gibberish in the std::string. What is wrong?

char char_buff[40];
sprintf_s(char_buff, 40, "test" );
printf("%s\n", char_buff); // prints "test"

std::string str(char_buff);
printf("%s\n", str); // prints random nonsense
Niall
  • 30,036
  • 10
  • 99
  • 142
Andreas
  • 7,470
  • 10
  • 51
  • 73

3 Answers3

12

Passing a std::string to printf gives undefined behavior.

When you try to print out the string instance, try using std::cout instead:

char char_buff[40];
sprintf_s(char_buff, 40, "test" );
std::cout << char_buff << "\n";

std::string str(char_buff);
std::cout << str << "\n";
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    You should also explain how to use it with printf – Neil Kirk Aug 11 '14 at 09:28
  • 7
    @NeilKirk: No, I shouldn't. While it's *possible* to use `printf` in C++, it's almost never a good idea. – Jerry Coffin Aug 11 '14 at 09:29
  • 2
    That's very judgemental. printf is still part of C++. I didn't say remove your advice about cout. – Neil Kirk Aug 11 '14 at 09:31
  • 3
    @NeilKirk Judgemental is relative. `printf` is so totally broken that I'd avoid it even in C. – James Kanze Aug 11 '14 at 09:32
  • `printf` is faster then `cout`. So it it should be used in c++ program if output speed is essential. – Ashot Aug 11 '14 at 09:34
  • 1
    @JamesKanze How is it "totally broken"? I know it is vulnerable to issues such as bad control strings. But so long as the parameters are correct, it works fine. – Neil Kirk Aug 11 '14 at 09:39
  • 2
    @Ashot: Speed of cout vs. printf varies widely. At one time, it was true that iostreams were consistently enough slower than C-style I/O that it was at least worth considering. That was a couple of decades ago though. – Jerry Coffin Aug 11 '14 at 09:41
  • @JerryCoffin I still find C-style file is faster than C++ fstream on VS2013 for heavy duty use – Neil Kirk Aug 11 '14 at 09:44
  • @NeilKirk: Then you're probably doing something wrong. The most common problem I've seen is unnecessary use of `std::endl`. FWIW, a test I ran years ago: http://stackoverflow.com/a/1926432/179910 – Jerry Coffin Aug 11 '14 at 09:49
  • I am referring only to file streams. – Neil Kirk Aug 11 '14 at 09:55
  • @JerryCoffin How is printf "totally broken"? – Andreas Aug 11 '14 at 09:58
  • @Andreas: I'm not the one who said it was totally broken, but a few obvious shortcoming include: 1) it's not type-safe (as you demonstrated) 2) it's not extensible 3) non-default formatting is compact but often quite unreadable. – Jerry Coffin Aug 11 '14 at 10:00
1
std::string str(char_buff);
printf("%s\n", str); // prints random nonsense

The problem is that %s makes printf() expect a const char*; in other words, %s is a placeholder for const char*.

Instead, you passed str, which is an instance of std::string, not a const char*.

To fix that, just use the c_str() method of std::string:

printf("%s\n", str.c_str());

c_str() returns a C-style pointer to a NUL-terminated string, as expected from C functions like printf().


As a side note:

char char_buff[40];
sprintf_s(char_buff, 40, "test" );

Note that sprintf_s() can be used in a simpler form, making it to automatically deduce the destination buffer length, thanks to some template "magic":

sprintf_s(char_buff, "test");  // char_buff size automatically deduced

See this sprintf_s() template from MSDN documentation:

template <size_t size>
int sprintf_s(
   char (&buffer)[size],
   const char *format [,
   argument] ... 
); // C++ only
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • 1
    Sorry, but "To fix that, just use the `c_str()` method of `std::string` is truly *lousy* advice. – Jerry Coffin Aug 11 '14 at 09:52
  • 2
    @JerryCoffin: Sorry, but your comment is _lousy_. If the guy wants to use `printf()` in C++ code, then he's free to do that, and to make `std::string` interop with `printf()` to my knowledge calling `c_str()` is just fine. – Mr.C64 Aug 11 '14 at 10:04
  • @JerryCoffin: Stupid comparison. There are several C++ programmers that use C `` in their C++ code, and there is no "shoot in the foot". On the same note, I could say you are "shooting yourself in the foot" using C++ I/O streams that are much slower and inefficient if compared to C ``. If you don't want to use `printf()` that's fine, but the question is about making `std::string` correctly interoperating with `printf()`. The OP is free to choose `printf()` or `std::cout`. Being respectful of other people's ideas is fundamental in a sane community. – Mr.C64 Aug 11 '14 at 14:15
  • I simply don't read the question as being about making `std::string` correctly interoperate with `printf`. I read it as being about converting a char array to `std::string`. I guess I should have read the: "What is wrong with this char array to std::string conversion?", more carefully to find the reference to `printf`. And no, as noted elsewhere, `printf` really isn't faster than iostreams either. "A sane community" doesn't require that I agree with your lavish praise of the emperor's new clothes. – Jerry Coffin Aug 11 '14 at 14:19
  • @JerryCoffin: In a sane community people can have different ideas, but they should respect each others, even if they disagree. I've spent enough time with stupid non-sensical comments including this last thing on the "emperor's clothes" (are you projecting something?). So I close this thing now. – Mr.C64 Aug 11 '14 at 14:56
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/59116/discussion-between-jerry-coffin-and-mr-c64). – Jerry Coffin Aug 11 '14 at 14:58
1

Unfortunately, printf is an old c function and is not type safe. It just interprets the given arguments as you tell it to.

By using the %s as the first occurring format specifier you tell printf to interpret the first argument as a char *, causing gibberish to be printed.

In order to have the contents of the string be printed you need to get to the char* data that the std::string wraps. You can do this using .c_str().

Tom Moers
  • 1,243
  • 8
  • 13