1

I have some integers, let's say one two and three. I wish to create a string such as

char* example = "There are " + one + " bottles of water on " + 
    two + " shelves in room number " + three + "\n".`  

This doesn't work in C/C++. How can I store that type of value in a char*?

Ólafur Waage
  • 68,817
  • 22
  • 142
  • 198
cilk
  • 231
  • 1
  • 2
  • 5
  • That's why I told you beforehand that they were integers. Would you rather I wrote "int one = 3; int two = 6; int three = 1;"? – cilk Oct 02 '10 at 17:00
  • Yes. Then we'd know that they are ints and not strings. – John Dibling Oct 02 '10 at 17:06
  • If you didn't tag this "C++" by accident, I should point out that in C++ it's almost always better to work with `string`s instead of `char*`s. – sepp2k Oct 02 '10 at 17:07
  • 3
    Alsso, there's no such language named C/C++. They are seperate languages, and the method you select depends on which language you're working in. So pick one. – John Dibling Oct 02 '10 at 17:07
  • @John Dibling - `int` is implicit in question title, by process of elimination... – Steve Townsend Oct 02 '10 at 17:08

6 Answers6

7

In C there's more than one way to do it, depending how you would like to allocate the memory[*]. For the straightforward option of allocating it from the heap:

len = snprintf(0, 0, "%d bottles, %d shelves, room %d\n", one, two, three);
char *result = malloc(len+1);
if (result == 0) { /* handle error */ }
snprintf(result, len+1, "%d bottles, %d shelves, room %d\n", one, two, three);

/* some time later */
free(result);

Beware non-standard implementations of snprintf, that don't return the length when the buffer is exceeded. Check your documentation.

In C++, snprintf is not in the standard, and even where it is available, the above code would need to cast the result of malloc[**]. C++ adds the option of using stringstreams:

std::stringsteam r;
r << one << " bottles, " << two << " shelves, room " << three << "\n";
std::string result = r.str();

// if you absolutely need a char*, use result.c_str(), but don't forget that
// the pointer becomes invalid when the string, "result" ceases to exist.

This saves messing with buffer lengths, makes resource management easier, and avoids the risk with printf and friends that you could pass an argument of the wrong type for the format specifier. It's usually the preferred option.

It is however less flexible in some circumstances: the format is hard-wired into the code rather than contained in a format string, so it's harder to make the text configurable. It can also be a bit harder to read, for instance it's not at all uncommon to leave out a space character on the first version of any such line of code. But if you want to use the snprintf approach in C++, and snprintf is available in your implementation, then you can take advantage of C++'s easier memory management as follows:

len = std::snprintf(0, 0, "%d bottles, %d shelves, room %d\n", one, two, three);
std::vector<char> r(len+1);
std::snprintf(&r[0], r.size(), "%d bottles, %d shelves, room %d\n", one, two, three);

char *result = &r[0];
// again, "result" is only valid as long as "r" is in scope

[*] Note that you cannot "store" a string in a char*, because a char* is just a pointer. You can store a pointer to a string in a char*, but the string itself is a completely separate thing.

[**] because C and C++ ARE DIFFERENT LANGUAGES!

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
6

C++ isn't VB. But you have numerous options.

stringstream:

#include <sstream>
#include <string>

stringstream ss;
ss<< "There are " << one << " bottles of water on " << two << " shelves in room number " << three;
string s = ss.str();

boost/format:

#include <boost/format.hpp>
#include <string>

string s = (boost::format("There are %1% bottles on %2% shelves in room number %3%")%one%two%three).str();

old-school (hacky, unsafe, bad, dont do it)

char buffer[1024] = {};
sprintf(buffer, "There are %d bottles on %d shelves in room number %d", one, two, three); 
John Dibling
  • 99,718
  • 31
  • 186
  • 324
1

sprintf is an option (as Ólafur wrote), but deprecated. Use snprintf when you can - sprintf can lead to buffer overruns and cause awful crashes and bugs if you're not very careful with the size of your buffer and inputs.

char output[256];
snprintf(output, sizeof(output), "There are %d bottles of water on %d shelves 
    in room number %d\n", one, two, three);
EboMike
  • 76,846
  • 14
  • 164
  • 167
  • "sprintf is good" yeah, ok. How did you come up with the magic number `256`? Is it big enough? It it wasteful? How can you be sure that `one`, `two` and `three` are `ints`? If somee maint programmer comes along and changes one of the types are you prepared to explode? – John Dibling Oct 02 '10 at 17:09
  • 1
    `snprintf` also deprecated in C++ – Steve Townsend Oct 02 '10 at 17:10
  • 3
    @Steve: I don't believe it is formally deprecated (and neither is `sprintf`). People prefer not to use it. – Steve Jessop Oct 02 '10 at 17:32
1
#include <sstream>

std::stringstream strVal;

strVal << std::string("There are ") << one << std::string(" bottles of water on ") << 
    two << std::string(" shelves in room number ") << three << std::endl;

std::string copyStr(strVal.str());
const char * example = copyStr.c_str();
Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
0

I like the stringstream approach, but I use a class to simplify its usage. See my answer to another SO question for more details:

string myString = MakeString() << "There are " << one << " 
      bottles of water on " << two << " shelves in room number " << three;

char * example = myString.c_str(); // if you really need a char *
Community
  • 1
  • 1
e.James
  • 116,942
  • 41
  • 177
  • 214
0

My solution:

template <typename T> std::string toStr(const T& something) {
    std::stringstream ss;
    ss << something;
    return ss.str();
}
...
std::string example = "There are " + toStr(one) + " bottles of water on " + 
                toStr(two) + " shelves in room number " + toStr(three) + "\n";
01d
  • 55
  • 4