tags:

views:

169

answers:

7

Hello all,

It's been a while since I have worked with C++, I'm currently catching up for an upcoming programming test. I have the following function that has this signature:

void MyIntToChar(int *arrayOfInt,char* output)

Int is an array of integers and char* output is a buffer that should be long enough to hold the string representation of the integers that the function receives.

Here is an example of the usage of such function:

int numbers[3] = {11, 26, 81};
char* output = "";        // this I'm sure is not valid, any suggestions on how to
                          // to properly initialize this string?   
MyIntToChar(numbers,output);
cout << output << endl;   // this should print "11 26 81" or "11, 26, 81".  
                          // i.e.  formatting should not be a problem.

I have been reviewing my old c++ notes from college, but I keep having problems with these. I'm hating myself right now for going to the Java world and not working in this.

Thanks.

A: 

You should take a look at the std::stringstream, or, more C-ish (as char* type instead of strings might suggest) sprintf.

Dario
I'll try it and let you know. Thanks
Jonathan Pierce
Allright, sprintf is working, however it only prints out the last number in the array. Any idea what could be wrong?
Jonathan Pierce
Then construct it in tiny pieces.
Dario
+1  A: 

Well an integer converted to string will require a max of 12 bytes including sign (assuming 32bit), so you can allocate something like this

char * output= new char[12*sizeof(numbers)/sizeof(int)];  
renick
I did not knew that you could initialize that pointer in that way. Although it makes sense, given the equivalence between pointers and arrays.
Jonathan Pierce
+1  A: 

First of all, it is impossible to use your method that way your example says: char* output has only a size of 1 byte (don't forget the null-terminator '\0'). So you can't put a whole string in it. You will get segmentation faults. So, here you are going to make use of the heap. This is already implemented in std::string and std::stringstream. So use them for this problem.

Let's have a look:

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

std::string intArrayToString(int *integers, int numberOfInts)
{
    std::stringstream ss;
    for (int i = 0; i < numberOfInts; i++)
    {
        ss << integers[i] << ", ";
    }
    std::string temp = ss.str();
    return temp.substr(0, temp.size() - 2); // Cut of the extra ", "
}

And if you want to convert it to char*, you can use yourString.c_str();

Martijn Courteaux
Thanks for your answer. I'm not sure if using stringstream and string inside the function will work for me.
Jonathan Pierce
@Jonathan: Why not? I think it is really the best way you can do it...
Martijn Courteaux
Well, I'm trying my best to avoid using string and other classes in the STL, as I was advised by the programmer I spoke with. He recommended that I should concentrate on C/C++ basics and then move to advanced topics like the STL.
Jonathan Pierce
@Jonathan: That's bullshit. Doing it manually is _much harder_ than doing it using the standard library (of which the STL is one part and `std::string` is _another_ part). If you ask me, try to get away from that programmer ASAP. Have a look at [The Definitive C++ Book Guide and List](http://stackoverflow.com/questions/388242/) and make your pick. For you, _Accelerated C++_ might be a good choice. It's got a _very_ steep learning curve, but since you already have C++ skills this shouldn't be a problem. At 250 pages it's a short introduction into the language _as it should be used_.
sbi
@sbi: Exactly. That is indeed bullshit. @Jonathan: Use the features you have available. If you take a look inside the string class, you will see that everything relies on basic C/C++. You are not going to tell me that you don't want to use the best and most simple way, provided you on hot sweat the C++ language developers.
Martijn Courteaux
A: 

did you try sprintf(),it will do your work.For char * initialization,you have to either initialize it by calling malloc or you can take is as a char array and pass the address to the function rather then value.

Anil Vishnoi
I'll try sprintf and will post my findings. Thanks!
Jonathan Pierce
Allright, sprintf is working, however it only prints out the last number in the array. Any idea what could be wrong?
Jonathan Pierce
+2  A: 
void MyIntToChar(int *arrayOfInt, char* output);

That's wrong in several ways. First, of all, it's a misnomer. You cannot, in general, convert an integer into one character, because only ten of all intergers (0...9) would fit into one. So I will assume you want to convert integers into _strings instead.

Then, if you pass arrays to functions, they decay to pointers to their first element, and all information about the array's size is lost. So when you pass arrays to function, you need to pass size information, too.
Either use the C way of doing this and pass in the number of elements as std::size_t (to be obtained as sizeof(myarray)/sizeof(myarray[0])):

void MyIntToStr(int *arrayOfInt, std::size_t arraySize, char* output);

Or do it the C++ way and pass in two iterators, one pointing at the first element (so-called begin iterator) and the other pointing to one behind the last (end iterator):

void MyIntToStr(int *begin, int *end, char* output);

You can improve on that by not insisting on the iterators being int*, but anything which, when dereferenced, yields an int:

template< typename FwdIt >
void MyIntToStr(FwdIt begin, FwdIt end, char* output);

(Templates would require you to implement the algorithm in an header.)

Then there's the problems with the output. First of all, do you really expect all the numbers to be written into one string? If so, how should they be separated? Nothing? Whitespace? Comma?
Or do you expect an array of strings to be returned?


Assuming you really want one string, if I pass the array {1, 2, 3, 4, 5} into your function, it needs space for five single-digit integers plus the space needed for four separators. Your function signature suggests you want me to allocate that upfront, but frankly, if I have to calculate this myself, I might just as well do the conversions myself. Further, I have no way of telling you how much memory that char* points to, so you can't check whether I was right. As generations of developers have found out, this is so hard to get right every time, that several computer languages have been invented to make things easier for programmers. One of those is C++, which nowadays comes with a dynamically resizing string class.
It would be much easier (for you and for me), if I could pass you a stirng and you write into that:

template< typename FwdIt >
void MyIntToChar(FwdIt begin, FwdIt end, std::string& output);

Note that I this passes the string per non-const reference. This allows you to modify my string and let's me see the changes you made.
However, once we're doing this, you might just as well return a new string instead of requireing me to pass one to you:

template< typename FwdIt >
std::string MyIntToChar(FwdIt begin, FwdIt end);

If, however, you actually wanted an array of strings returned, you shouldn't take one string to write to, but a means where to write them to. The naive way of doing this would be to pass a dynamically re-sizable array of dynamically re-sizable string. In C++, this is spelled std::vector<std::string>:

template< typename FwdIt >
void MyIntToStr(FwdIt begin, FwdIt end, std::vector<std::string>& output);

Again, it might be better you return such an array (although some would disagree since copying an array of string might be considered to expensive). However, the best way to do this would not require me to accept the result in form of a 'std::vector'. What if I needed the strings in a (linked) list instead? Or written to some stream?
The best way to do this would be for your function to accept an output iterator to which you write your result:

template< typename FwdIt, typename OutIt >
void MyIntToStr(FwdIt begin, FwdIt end, OutIt output);

Of course, now that's so general that it's hard to see what it does, so it's good we gave it a good name. However, looking at it I immediately think that this should build on another function which is needed probably even more than this one: A function that takes one integer and converts it to one string. Assuming that we have such a function:

std::string MyIntToStr(int i);

it's very easy to implement the array versions:

template< typename FwdIt, typename OutIt >
void MyIntToStr(FwdIt begin, FwdIt end, OutIt output)
{
  while(begin != end)
    *output++ = MyIntToStr(*begin++);
}

Now all that remains for you to be done is to implement that std::string MyIntToStr(int i); function. As someone else already wrote, that's easily done using string streams and you shouldn't have a problem to find some good examples for that. However, it's even easier to find bad examples, so I'd rather give you one here:

std::string MyIntToStr(int i);
{
  std::ostringstream oss;
  oss << i:
  if(!oss) throw "bah!"; // put your error reporting mechanism here
  return oss.str();
}

Of course, given templates, that easy to generalize to accepting anything that's streamable:

template< typename T >
std::string MyIntToStr(const T& obj);
{
  std::ostringstream oss;
  oss << obj:
  if(!oss) throw "bah!"; // put your error reporting mechanism here
  return oss.str();
}

Note that, given this general function template, the MyIntToStr() working on arrays now automatically works on arrays of any type the function template working on one object works on.


So, at the end of this (rather epic, I apologies) journey, this is what we arrived at: a generalized function template to convert anything (which can be written to a stream) into a string, and a generalized function template to convert the contents of any array of objects (which can be written to a stream) into a stream.

Note that, if you had at least a dozen 90mins lectures on C++ and your instructors failed to teach you enough to at least understand what I've written here, you have not been taught well according to modern C++ teaching standards.

sbi
I believe you forgot the ++ operator in `MyIntToStr`. `output` iterator is not advanced.
Maciej Hehl
@Maciej: Indeed, thanks! I fixed it.
sbi
+1  A: 

Here's a possibility if you are willing to reconsider a change in prototype of the function

template<int n>
void MyIntToChar(int (&iarr)[n], string &output){
    stringstream ss;
    for(size_t id = 0; id < n; ++id){
        ss << iarr[id];
        if(id != n - 1) ss << " ";
    }
    output = ss.str();
}

int main(){
    int numbers[3] = {11, 26, 81};
    string out = "";
    MyIntToChar(numbers, out);
}
Chubsdad
A: 

Sounds like you would like to use C lang. Here's an example. There's an extra ", " at the end of the output but it should give you a feel for the concept. Also, I changed the return type so that I would know how many bytes of output were used. The alternative would be to initialize output would nulls.

int MyIntToChar(int *arrayOfInt, char* output) {  
    int bytes_used = 0; // use to bump the address past what has been used  
    for (int i = 0 ; i < sizeof(arrayOfInt); ++i)  
        bytes_used += sprintf(output + bytes_used, "%u, ", arrayOfInt[i]);  

    return bytes_used;  
}  

int main() {    
    int numbers[5] = {5, 2, 11, 26, 81};    // to properly initialize this string?  
    char output[sizeof(int)*sizeof(numbers)/sizeof(int) + sizeof(numbers)*2]; // int size plus ", " in string  

    int bytes_used = MyIntToChar(numbers, output);  
    printf("%*s", bytes_used, output);// this should print "11 26 81" or "11, 26, 81".  
  }    
skimobear