1

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.

  • 5
    Whats the dislike of std::string and std::vector? (Although if you think that sort of thing will come up in the test, fair enough) – Yacoby Aug 29 '10 at 09:22
  • Is `void MyIntToChar(int *arrayOfInt,char* output)` part of the assignement because it looks like C to me, `void MyIntToChar(std::vector in, std::string out)` feels more like CPP to me. You should also clarify your question. –  Aug 29 '10 at 09:25
  • I'm positive that the test skips std::string and vector. I want to get my bases covered and then (if I have time) review the containers and string class in the STL – Jonathan Pierce Aug 29 '10 at 09:26
  • 1
    It's C++, but they were very clear that I should review C. – Jonathan Pierce Aug 29 '10 at 09:27
  • I don't think this is an assignement. It seems more like just he's training by himself. – Federico klez Culloca Aug 29 '10 at 09:27
  • Klez you are correct. I'm just reviewing for an upcoming test. – Jonathan Pierce Aug 29 '10 at 09:35
  • 1
    @RC: The way you needlessly copy a vector, your latter prototype looks more like Java to me. `:)` – sbi Aug 29 '10 at 10:22

7 Answers7

2
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
  • 219,715
  • 46
  • 258
  • 445
1

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
  • 3,873
  • 2
  • 31
  • 40
  • 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 Aug 29 '10 at 09:30
1

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
  • 67,591
  • 47
  • 198
  • 287
  • Thanks for your answer. I'm not sure if using stringstream and string inside the function will work for me. – Jonathan Pierce Aug 29 '10 at 09:39
  • 1
    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 Aug 29 '10 at 09:48
  • 3
    @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 Aug 29 '10 at 10:29
  • @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 Aug 29 '10 at 10:59
1

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
  • 24,777
  • 4
  • 73
  • 129
0

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

Dario
  • 48,658
  • 8
  • 97
  • 130
0

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
  • 1,352
  • 3
  • 18
  • 25
0

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
  • 1,188
  • 10
  • 12