0

I have two problems. Both can be illustrated by this simple code (compiled with g++ -Wall -O2 -std=c++14):

#include <string>
using namespace std;

const char *printi(const unsigned long &i) {
    return to_string(i)).c_str();
}

int main() {    
    unsigned long i1 = 1; unsigned long i2 = 2;
    fprintf(stderr, "%s%s\n", printi(i1), printi(i2));
}

Problem no. 1 - It prints "11", while it should print "12".

Problem no. 2 - Sometimes it doesn't print numbers at all, but some weird characters instead.

Jecke
  • 239
  • 2
  • 13
  • Interestingly when I run this code I get 22 as my output. Why are you printing to `stderr` instead of `stdout`? – Eli Sadoff Oct 19 '16 at 20:58
  • You're returning a temporary = bad news. – AndyG Oct 19 '16 at 21:00
  • How can I work if I can't use the values my function returns? :( I need to put that string into fprintf... (the whole code is just an abstraction of something much bigger, and this is supposed to be a debug information, that's why it is stderr) – Jecke Oct 19 '16 at 21:07
  • @Jecke: Are you writing C or C++? Use a `std::string`, use `std::cerr` if you're writing C++ – AndyG Oct 19 '16 at 21:10
  • I'm writing in C++, but I use fprintf because it's much more convenient. I can have `fprintf("blah %s blah %lu blah %s\n", a, b, c)` instead of `cerr << "blah" << a << "blah" << b << "blah" << c << endl;`... it's much shorter and flexible. In fact, I have `#define debug_out( ... ) if (debug) fprintf(stderr, __VA_ARGS__ )` at the beginning and I don't have to worry about how much different arguments I want to print and it's much much cleaner – Jecke Oct 19 '16 at 21:23
  • Please don't edit the solution into the question. Answers go in the Answer box. You can either accept someone's answer, or post and accept your own answer. – M.M Oct 19 '16 at 21:33
  • BTW in C++ you can rig up something like `tprint("blah % blah % blah", a, b, c);` where you don't need a format specifier because it detects the format from the type of the argument – M.M Oct 19 '16 at 21:34
  • ok, sorry. How could I do that? – Jecke Oct 19 '16 at 21:39

1 Answers1

4

Notice that std::to_string() returns a std::string. This std::string exists only inside of the printi() function scope, which means when you exit the function (when you return), the destructor of this std::string is called, and the address you return (the value returned by c_str()) no longer points to valid memory. Your code has undefined behavior, because you don't know to which data it is now pointing.

In order to fix this, you can have printi() return a std::string instead, which will be deep copied into the caller upon return, and then the caller can use c_str() as needed:

#include <string>
using namespace std;

string printi(const unsigned long &i) {
    return to_string(i);
}

int main() {    
    unsigned long i1 = 1; unsigned long i2 = 2;
    fprintf(stderr, "%s%s\n", printi(i1).c_str(), printi(i2).c_str());
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Sawel
  • 929
  • 7
  • 17
  • True. Fixed. Thanks. – Sawel Oct 19 '16 at 21:01
  • Hmm... so if I create a function int return5() { short a = 5; return a; }, I cannot expect that cout << 2*return5(); will print 10? Or does this only not work with strings? – Jecke Oct 19 '16 at 21:03
  • *"In order to fix it you can return a reference to std::string"* -- It sounds like you're suggesting replacing a dangling pointer with a dangling reference. If not, I'm not sure what you're suggesting here. – Benjamin Lindley Oct 19 '16 at 21:07
  • Well this will actually work, when you return an object (char*, int) then it is just copied, for int it will copy its data, for char* it will copy its address. If you return std::string it will copy its data as well But when you exit the scope, the destructor is called, and now the address of the c_str string, is not valid anymore, the memory was cleaned! Now you hold in your hands non valid address and you try to print it. – Sawel Oct 19 '16 at 21:09
  • @Jecke: A better analogy would be if you created a function like this: `int* return5() { int a = 5; return &a; }` -- In which case you have no guarantee that `cout << *return5();` will print 5. – Benjamin Lindley Oct 19 '16 at 21:16
  • I see, thanks :) I changed the return type to string and now I have fprintf(...printi(i1).c_str()...) and it works! – Jecke Oct 19 '16 at 21:18