views:

361

answers:

8

I'm working on a C++ application that will build a fixed-length record from a set of database fields. I'm writing a function that will accept the output record as a char*, the string to write, and the total length of the field. The purpose of the function is to copy the string to the current position of the char pointer, and then fill the rest of the length with spaces. Here's a simplified example of what I'm doing.

void writeOut(char* output, string data, const int length) {
    if ((int) data.size() > length) {
        //Just truncate it
       data = data.substr(0, length);
    }
    int index = 0;
    while (index < (int) data.size()) {
        *output++ = data[index++];
    }
    while (index++ < length) {
        *output++ = ' ';
    }
}

int test() {
    char output[100];
    writeOut(output, "test1", 10);
    writeOut(output, "test2", 10);
    writeOut(output, "test3test4test5", 10);
    cout << output;
}

I expected to see something like this.

test1     test2     test3test4

Instead all I get is...

test3test4

So it's incrementing the char* within the function, but only within the function. When the function ends the char* is right back where it started. Is it possible to pass a pointer in such a way that the pointer is updated in the calling function?

In case you can't tell, I'm pretty new to C++. Any suggestions will be appreciated.

+6  A: 

You can accomplish what you are after by keeping a new character pointer, but you can't increment the "output" variable and then use it again unincremented (in your cout line).

For example, you could do something like:

char* writeOut(char* output, string data, const int length) {
    if ((int) data.size() > length) {
        //Just truncate it
       data = data.substr(0, length);
    }
    int index = 0;
    while (index < (int) data.size()) {
        *output++ = data[index++];
    }
    while (index++ < length) {
        *output++ = ' ';
    }
    return output; // return the new position here!
}

int test() {
    char output[100];
    char *outputLocation = output;
    outputLocation = writeOut(outputLocation, "test1", 10);
    outputLocation = writeOut(outputLocation, "test2", 10);
    outputLocation = writeOut(outputLocation, "test3test4test5", 10);
    cout << output;
}
Reed Copsey
+1 Although other answers provided answers to the question, I think you provided a better approach :)
AraK
Thanks. I prefer this approach since it makes incrementing the pointer optional, not required, in order to use the method.
Reed Copsey
+1, thanks. I thought about this approach, but couldn't figure out the syntax for it. This is a good idea.
John M Gant
+8  A: 

Since you are not passing your arguments by reference, the compiler creates a copy of the pointer and modifies the copy accordingly within the function.

Change your function signature to the following.

void writeOut(char*& output, string data, const int length)

You may also want to consider passing string as const string& if you don't plan to modify it.

Steve Guidi
Just be sure to pass in a char *, not a char [], since you can't increment the value of an array.
David Thornley
Also: This would break his cout << output line, unless he makes a copy of the pointer. You'd have to make a char* that points to output, then use that.
Reed Copsey
Thanks. For some reason I assumed you couldn't pass a reference to a pointer.
John M Gant
+7  A: 

You want to pass a char** to the function.

void writeOut(char** output, string data, const int length) {
    if ((int) data.size() > length) {
        //Just truncate it
       data = data.substr(0, length);
    }
    int index = 0;
    while (index < (int) data.size()) {
        *(*output)++ = data[index++];
    }
    while (index++ < length) {
        *(*output)++ = ' ';
    }
}

int test() {
    char output[100];
    char *pos = output;
    writeOut(&pos, "test1", 10);
    writeOut(&pos, "test2", 10);
    writeOut(&pos, "test3test4test5", 10);
    cout << output;
}

(No compiler on hand, but this should work)

developmentalinsanity
+1 because this seems to be a more conventional way to accomplish this
Tony
I think pos has to be char** pos =
Douglas Leeder
Richard Corden
Reed Copsey
@Tony: More conventional? In 'C' this is the only way, but in C++ we have references which thankfully help us avoid the need to wrap our minds round too many layers of indirection!
Richard Corden
+1, thanks. I knew there was a "pointer to a pointer" way of doing it, but I couldn't get the syntax quite right.
John M Gant
@Richard Corden: You're right. That's what I meant it to be. Edited.
developmentalinsanity
A: 

You need to pass by reference.

Floetic
A: 

The important thing to understand is that arguments to functions are passed by value, that is they are copied.

The first argument to your function is a pointer, in other words a number that points to memory. When you call the function, this number is copied from test() to printOut(), which allows printOut() to access the memory that test() allocated.

As others have suggested, you can either return the new value of the pointer, or pass it in as a reference. Or else you can use a pointer to the pointer which will allow you to update the pointer in printOut().

Douglas Leeder
+2  A: 

What's happening is that each every time you call writeOut you're passing the same position (ie. the start) to the call.

What you need is another variable which you move along as you call writeOut. There are several ways to do this:

The first and "easiest" option is to make the parameter you pass in a "reference to pointer":

void writeOut (char *& output, ...) {
   // ...
}

int test () {
  char output[100];
  char * p = output;
  writeOut (p, "test1", 10);
  writeOut (p, "test2", 10);
  // ...
}

This works because the parameter output is now an alias to the pointer p in the body of main. As a result the changes made to the parameter in writeOut are also happening to p.

The next easiest option is to return the new position each time:

char * writeOut (char * output, ...) {
   // ...


   return output;
}

int test () {
  char output[100];
  char * p = output;
  p = writeOut (p, "test1", 10);
  p = writeOut (p, "test2", 10);
  // ...
}

Here, the parameter is modified inside the function and we update p in main each time.

Finally you could make the parameter on writeOut a pointer to pointer:

void writeOut (char ** output, ...) {
   // ...

   while (index < (int) data.size()) {
     **output = data[index++];
     (*output)++;
   }

   // ...
}

int test () {
  char output[100];
  char * p = output;
  writeOut (&p, "test1", 10);
  writeOut (&p, "test2", 10);
  // ...
}

What's happening here is that our parameter no longer points to 'output', but rather it points to the address of 'p'. I would almost certainly go with the reference option before I'd consider this.

Richard Corden
+1, thanks for explaining the differences among the different options.
John M Gant
A: 

Why not just use snprintf?

int writeOut(char *output, char *data, int length)
{
  return snprintf(output, length + 1, "%-*s", length, data);
}

void test()
{
  char output[100];
  char *outputLocation = output;
  outputLocation += writeOut(outputLocation, "test1", 10);
  outputLocation += writeOut(outputLocation, "test2", 10);
  outputLocation += writeOut(outputLocation, "test3test4test5", 10);
  printf("%s\n", output);
}

I think it's easier. Up to you, I guess.

Carl Norum
Vastly improved my original submission thanks to a clever coworker. Thanks Steve!
Carl Norum
It hadn't occurred to me that I could use printf like this. One question though. All of my input data is in string format. Would printf not introduce some overhead as compared to simply moving characters from a string to a character array?
John M Gant
Unfortunately I don't know C++ string details that much. I would guess you have a lot of overhead based on STL strings that would be improved by using C-style character arrays.
Carl Norum
A: 

changing it to:

void writeOut(char& *output, string data, const int length)
    if ((int) data.size() > length) {
        //Just truncate it
       data = data.substr(0, length);
    }
    int index = 0;
    while (index < (int) data.size()) {
        *output++ = data[index++];
    }
    while (index++ < length) {
        *output++ = ' ';
    }
}

_

int test() {
        char output[100];
        writeOut(output, "test1", 10);
        writeOut(output, "test2", 10);
        writeOut(output, "test3test4test5", 10);
        cout << output;
    }


Eric A Booth | [email protected]

eric.a.booth