2

I am trying to convert a stringstream into a const char*, but I always get a blank string.

My Code:

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

const char* test(std::string city, std::string street, int houseNumber, int postCode) {
std::stringstream ss;
ss << city << " " << street << " " << houseNumber << " " << postCode;
const std::string tmp =  std::string{ss.str()};
const char* str = tmp.c_str();
return str;
}

int main(){
printf(test("demo", "Demo", 1, 1234));
}

Expected Output: demo Demo 1 1234

I tried all the enhancements mentioned in How to convert a std::string to const char* or char*?, but I had no success.

Jan
  • 407
  • 4
  • 16

3 Answers3

5

The problem is that the string data your const char* points to has been freed after the return str;. The const char* pointer will stay valid as long as the associated std::string instance is within scope. This is assuming the code you have shown here is located within a function. In that case, the string tmp will go out of scope after the return str;, and its destructor will be called, causing any pointer pointing to its character data to become a dangling pointer.

Return an std::string, and call c_str() on the returned string instead. As mentioned, you can then use the const char* pointer as long as the associated std::string instance is within scope.

Ton van den Heuvel
  • 10,157
  • 6
  • 43
  • 82
1

The problem you're encountering is that when this function leaves scope (i.e. when return str; executes), the memory str points to will have been cleaned up. You need to return an object that will maintain ownership over the data.

//std::string_view is >=c++17, use std::string const& for older compilers
std::string get_formatted_address(
    std::string_view city, 
    std::string_view street, 
    std::string_view houseNumber, 
    std::string_view postCode
) {
    std::stringstream ss;
    ss << city << " " << street << " " << houseNumber << " " << postCode;
    return ss.str();
}

int main() {
    auto city = "New York", street = "Rockefeller Lane", houseNumber = "1234", postcode = "56789";
    auto formatted_address = get_formatted_address(city, street, houseNumber, postcode);
    //Here, formatted_address holds ownership over the memory, so calling c_str is safe
    printf("Address: %s\n", formatted_address.c_str());
}
Xirema
  • 19,889
  • 4
  • 32
  • 68
1

You can turn dangling references into non-dangling references by using static thread_local to store a temporary object.

Note that you will only be able to have one active "non dangling" reference at a time:

#include <string>
#include <sstream>
#include <iostream>

const char* to_cstr(std::string && s)
{
    static thread_local std::string sloc;
    sloc = std::move(s);
    return sloc.c_str();
}

const char* make_address(std::string const& city, 
                         std::string const& street, 
                         std::string const& houseNumber, 
                         std::string const& postCode)
{
    std::stringstream ss;
    ss << city << " " << street << " " << houseNumber << " " << postCode;
    return to_cstr(std::move(ss).str());
}

int main()
{
    const char* p = make_address("London","Bob Street","42","W1ABC");
    std::cout << p << std::endl;
}

expected output:

London Bob Street 42 W1ABC

http://coliru.stacked-crooked.com/a/228e6c0cb8359222

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142