Getting different output with printf and cout - C++

13,589

Solution 1

it's because rawname is defined as a std::string. You need to use

printf("rawname: %s", rawname.c_str());

The reason is that printf with the %s is expecting a null terminated C string in memory. Whereas a std::string stl string isn't exactly raw - it eventually null terminates in your situation, not sure if that's even a guarantee, since the length is internally managed by stl container class.

Edit:

As pointed out in a comment, internally it's guaranteed to be null terminated. So what you're seeing as 'squiggly lines' is an output of all the allocated but not utilized (or initialized) memory in that string up until the null terminator character.

Solution 2

What works

printf("%s", my_string.c_str());

What was wrong - synopsis

Short illustration (assumptions explained later):

std::string s {
   // members in unknown order
   size_type member:    13 00 00 00                       HEAP
   const char* member:  pointer C to ................ "this and that"
};

You print characters here ^^^^^^       not          here ^^^^^.

You can't pass non-POD data to functions - such as printf() - that accept arbitrary numbers of arguments using .... ("..." parameters is a feature C++ inherits from C, and it's inherently unsuitable for use with complicated C++ objects).

You can even compile that?

My GCC compilers don't like this:

printf("rawname: %s", rawname);

GCC 4.5.2 error:

cannot pass objects of non-trivially-copyable
type 'struct std::string' through '...'

GCC 4.1.2 warning + runtime behaviour:

cannot pass objects of non-POD type 'struct std::string'
through '...'; call will abort at runtime

# ./printf_string
zsh: illegal hardware instruction  ./printf_string

They won't compile it, because there is no standard way to pass objects using .... The compiler can't work out from simply ... whether they're needed by value or by reference/pointer, so won't know what code to generate.

But your compiler bravely did something. Let's consider what the std::string object looks like for a moment, then return to how your compiler might have received and accessed it.

The gizzards of a std::string object

The internals of a std::string aren't specified, but typically contain any of:

  • a member recording the current size OR a pointer past the end of the string (ala end())
    • either allows simple calculation of the other, but the couple Standard Library implementations I've checked optimise for a pointer/end() member and calculated size() - works better with idiomatic iterator loops
  • a pointer to a character buffer on the heap (in practice it's likely kept NUL terminated and c_str() returns it directly, but this pointer - available via the data() member function, is allowed by the Standard to address non-NUL terminated text, so theoretically it could have a NUL terminator appended only when c_str() is called, or c_str() might copy the text elsewhere then append the NUL and return a pointer to that new buffer)
  • a "short string optimisation" data buffer so strings of only a few characters need not use the heap

and/or

  • a pointer to some reference-counted object elsewhere (that has the members above + a reference counter, a mutex, ...?)

Example: a simple string implementation storing text

These could be in any order. So, the simplest possibility is something like:

std::string s = "this and that";

Now,

  • "this and that" is a string literal, let's say at address "A"; this data is copied into the string; the string does not remember where it got it from

  • s is the actual std::string object, let's say at address "B"; let's imagine it's the simplest possible:

    • size_type size_; (will hold the value 13, being strlen("this and that"))
    • const char* p_data_; will point to some newly allocated heap memory - let's say at address "C" - into which "this and that\0" has been copied

Crucially, address "A", address "B" and address "C" are different!

How printf() sees the std::string

If we had a bad compiler that would attempt to pass our std::string object to printf(), then there are two things printf() might receive instead of the const char* that "%s" tells it to expect:

1) a pointer to the std::string object, i.e. address "B"

2) sizeof(std::string) bytes of data copied from address "A" to some stack address "B" and/or registers where printf() would expect it if it could handle these things ;-P

printf() then starts printing the bytes from that address as if they're characters until it finds a 0/NUL byte:

  1. for scenario 1 above, it prints the bytes in the object, for example:

    • say size_type is 4 bytes and at the start of the object; with size 13 it might be 13, 0, 0, 0 or 0, 0, 0, 13 depending on whether the machine uses the big-endian or little-endian storage convention... given it stops at the first NUL, it would print character 13 (which happens to be an ASCII carriage-return/CR value, returning the cursor to the start of line) then stop, or it might print absolutely nothing. In your own case your string content were different, so it would have printed some other garbage, but probably only a character or two before hitting a 0/NUL.

    • say a const char* to the heap-allocated buffer at "C" happens to be at the start of the object, then the individual characters in that address would be printed: for 32-bit pointers that's probably 4 garbage characters (assuming none of them happen to be 0/NUL), for 64-bit it'll be 8, then it'll continue with the next field in the std::string (likely a end()-tracking pointer, but if it's a size_type field that's much more likely to have a 0/NUL).

  2. printf() might interpret the first four bytes of the std::string object's data as a pointer to further textual data... this is a different from 1): say the size_type member was first and the value was 13, printf() could mis-interpret that as a const char* to address 13, then attempt to read characters from there. This is practically guaranteed to crash before printing anything (on modern OSes), so it's very unlikely that this behaviour actually happened, which leaves us with "1".

Solution 3

You need to print the internal char* of the std::string:

printf("rawname: %s", rawname.c_str());

Solution 4

Try this

cout << "rawname:" << rawname << endl;
printf("rawname: %s", rawname.c_str());

rawname is not a char array, but an instance of the std::string class. To get the actual char array, you should call the c_str() function

Solution 5

Have you tried rawname.c_str() in the printf?

Share:
13,589

Related videos on Youtube

Scrimshaw Rest
Author by

Scrimshaw Rest

Updated on June 01, 2022

Comments

  • Scrimshaw Rest
    Scrimshaw Rest almost 2 years

    I have a string I am trying to print. when I used cout, it outputs perfectly but using printf leaves it mangled.

    Here is the code:

    int main ( int argc, char *argv[] )
    {
        // Check to make sure there is a single argument
        if ( argc != 2 )
        {
            cout<<"usage: "<< argv[0] <<" <filename>\n";
            return 1;
        }
    
        // Grab the filename and remove the extension
        std::string filename(argv[1]);
        int lastindex = filename.find_last_of("."); 
        std::string rawname = filename.substr(0, lastindex);
    
        cout << "rawname:" << rawname << endl;
        printf("rawname: %s", rawname);
    
    }
    

    The cout gives me "rawname: file"
    The printf gives me "rawname: " and then a bunch of squiggly characters

  • Nicol Bolas
    Nicol Bolas almost 13 years
    Yes, std::string::c_str() is guaranteed by the standard to be null-terminated.
  • Tony Delroy
    Tony Delroy almost 13 years
    that solution's great, but explanation misleading. There's no "raw" (versus "cooked"?) textual representations involved, it's just that the printf()'s likely printing the bytes in the std::string object itself (e.g. a number storing the size, a pointer to the actual text) rather than following that pointer to the text value. Because it's not even looking at the string's text, it prints garbage until there's coincidentally a 0 byte it can interpret as a NUL (thankfully common in the size member variable).
  • Tony Delroy
    Tony Delroy almost 13 years
    @Nicol Bolas: c_str() is guaranteed NUL terminated, but the original code didn't use c_str() and even if it had somehow got access to the string's text buffer - i.e. the %s argument pointed to rawname.data() - that wouldn't have been guaranteed NUL terminated - though in any sane implementation it would have been anyway ;-)
  • Matthieu M.
    Matthieu M. almost 13 years
    actually, I think the OP is seeing some binary because the "real" characters are stored in a dynamically allocated array (usually) and not directly in the string.