Reputation: 15
see simplified code - I'm confused...
#include <stdio.h>
#include <string>
#include <cstdlib> // includes the standard library and overarchs over stdlib.h
using namespace std;
void main()
{
char buffer[10];
string theString;
int i = 997799; //(simplified)
itoa(i,buffer,10);
theString = buffer;
printf("\n string is: %s of length %d \n", theString, theString.length());
printf("\n buffer is: %s of length %d \n", buffer, theString.length());
return;
}
The Output I get is unexpected:
string is: (null) of length 926366009
buffer is: 997799 of length 6
(1) why is the string printing as null?
(2) why is theString.length() not printing properly in the first printf() but is right in the 2nd?
(3) if I set breakpoints in visual studio 'buffer' appears as "997799" while 'theString' appears as {"997799"} - something weird going on here?
Thanks folks! Edit I greatly appreciate the level of detail of the provided answers - they all added clarity and helped me go beyond my issue - many thanks for the time you spent helping out :)
Upvotes: 1
Views: 6556
Reputation: 6777
As another answer pointed out, the reason for the (null)
value is because the (formal) printf
%s
expects a const char*
for its input, a std::string
is not a const char*
and results in undefined behavior... The reason for the random number when you do your first theString.length()
has to do with your compiler.
On OpenBSD 5.1 with g++ 4.2.1, when I compile the code I get numerous warnings; one in particular is use of itoa
, I had to change to sprintf
, I also get the following warning about %s
with printf
on a std::string
: warning: cannot pass objects of non-POD type 'struct std::string' through '...'; call will abort at runtime
.
When I run the compiled code it aborts and core dumps at the first printf
because of the call to %s
on a std::string
'object' (which is the 'correct' behavior though technically still 'undefined behavior')
However, when I compile the above code (without edits) on an MS compiler, I actually get the following output:
string is: 997799 of length 6
buffer is: 997799 of length 6
Which is still 'undefined' but the 'expected' results.
So apparently the MS compiler recognizes the std::string
on a printf
and 'changes' it to a string.c_str()
call OR the printf
in the MS C/C++ library accepts a std::string
and calls the .c_str()
for you.
So to be 100% 'compatible' and C++ 'compliant' consider the following:
#include <iostream>
#include <sstream>
int main(int argc, char **argv)
{
std::stringstream theString;
int i = 997799; //(simplified)
theString << i;
std::cout << "string is: " << theString.str() << " of length " << theString.str().length() << std::endl;
// printf("string is: %s of length %d \n", theString.str().c_str(), theString.str().length());
return 0;
}
It's usually not good practice to mix C++ and C calls/styles, but IMHO I use printf
a lot for it's 'convenience' but usually take it out or use some #defines
for debug build use only.
I hope that can help answer the 'why for #2'
Upvotes: 1
Reputation: 81996
My compiler (clang on os x) reports the following errors:
c++ foo.cc -o foo
foo.cc:6:1: error: 'main' must return 'int'
void main()
^~~~
int
foo.cc:11:5: error: use of undeclared identifier 'itoa'
itoa(i,buffer,10);
^
foo.cc:14:48: error: cannot pass non-POD object of type 'string' (aka
'basic_string<char, char_traits<char>, allocator<char> >') to variadic
function; expected type from format string was 'char *'
[-Wnon-pod-varargs]
printf("\n string is: %s of length %d \n", theString, theString.length());
~~ ^~~~~~~~~
foo.cc:14:48: note: did you mean to call the c_str() method?
printf("\n string is: %s of length %d \n", theString, theString.length());
^
.c_str()
foo.cc:14:59: warning: format specifies type 'int' but the argument has type
'size_type' (aka 'unsigned long') [-Wformat]
printf("\n string is: %s of length %d \n", theString, theString.length());
~~ ^~~~~~~~~~~~~~~~~~
%lu
foo.cc:15:56: warning: format specifies type 'int' but the argument has type
'size_type' (aka 'unsigned long') [-Wformat]
printf("\n buffer is: %s of length %d \n", buffer, theString.length());
~~ ^~~~~~~~~~~~~~~~~~
%lu
Let's fix them:
void main() { ... }
should be int main() { ... }
.
itoa()
is not standard in C++. There is std::stoi()
though, so let's use that instead. We could also use std::snprintf
if we are writing to a character array, and not a std::string
.
If we're going to use printf()
with a std::string
, we will need to use the std::string::c_str()
method to return a character array and print that instead. printf itself doesn't understand C++ objects. (We could also use cout, which does understand c++ objects).
These changes cause the code to look like:
#include <cstdio>
#include <string>
#include <cstdlib>
using namespace std;
int main()
{
char buffer[10];
string theString;
int i = 997799; //(simplified)
snprintf(buffer, sizeof(buffer), "%d", i);
theString = buffer;
printf("string is: %s of length %lu\n", theString.c_str(), theString.length());
printf("buffer is: %s of length %lu\n", buffer, strlen(buffer));
}
And this code outputs:
[4:46pm][wlynch@watermelon /tmp] ./foo
string is: 997799 of length 6
buffer is: 997799 of length 6
Upvotes: 0
Reputation: 153955
When you use the %s
specifier with printf()
you promise to pass a char const*
as the corresponding argument. Passing anything except a char const*
or something which decays into a char const*
is undefined behavior. Certainly, passing a C++ object will have undefined behavior.
The proper way to pass a std::string
to printf()
is to use the %s
format specifier and use the c_str()
member, e.g.:
printf("string=%s\n", s.c_str());
You are using %d
as a format specifier for std::string::size_type
. That will probably work but it isn't guaranteed to work! Although std::string::size_type
is guaranteed to be std::size_t
, this type may be unsigned int
, unsigned long
, unsigned long long
, or even some non-standard built-in integral type! The proper way to spell the format specifier for std::size_t
is %zu
(and certainly not %ul
as in another post: it was probably meant to be %lu
which is, however, still wrong:
printf("string.size()=%zu\n", s.size());
Since you are using C++, you are probably better off having the compiler figure out what formatting to call:
std::cout << "\n string is: " << theString << " of length " << theString.length() << " \n";
std::cout << "\n buffer is: " << buffer << " of length " << theString.length() << " \n";
Upvotes: 5
Reputation: 311068
The program has undefined behaviour because function printf is unable to output objects of type std::string. When format symbol %s is used the function supposes that the corresponding argument is a pointer of a string literal. So the function tries to output the object of type std::string as a string literal. To use correctly function printf with objects of type std::string you should convert them to strings using member function c_str() or data() (for C++ 2011). For example
printf("\n string is: %s of length %ul \n", theString.c_str(), theString.length());
Upvotes: 3