2

This seems like a question that would have been asked before, but I couldn't find it. (If you do, please point me there and close this as a duplicate.)

I have C code which works something like this:

printf("some characters");
for (; n >= 0; n--)
{
    printf(", %s", f(n, k));
}
printf("more characters");

for some variables n and k and function char* f(int n, int k) which are not important here. (n and k are not constant.) I would like to convert the code above to a function which returns a char* rather than simply using printf to display it. (It will ultimately be printed but this lets me do some refactoring and is of course easier to test that way.) Of course I could just create the strings and copy them over character-by-character but surely there is a better (cleaner, faster, more idiomatic) way of doing this.

In my particular application f is a very simple function, sort of a relative of itos, but which is not cacheable in any meaningful way due to k. But I welcome ideas which are good for that case as it may be useful to others in the future.

Charles
  • 11,269
  • 13
  • 67
  • 105
  • You can either pass a buffer and length to the function (similar to `snprintf`), or you need to `malloc` a buffer in the function, and then `free` it in the calling code. The third option is to use a `static` or global buffer, but that usually ends badly. Beyond that, there's not much to say, unless you tell us more about the function. And btw, what the heck is `pari_printf`? – user3386109 Aug 18 '16 at 04:35
  • 1
    function `char* f(int n, int k)` ... I would like to convert this to a function which returns a `char*`. I do not get the question. It already returns a `char*` – Rishikesh Raje Aug 18 '16 at 04:46
  • 1
    @user3386109 The actual function in my code (as opposed to the cleaned-up version above) is converting chunks of a large array into a string. `snprintf` might be an option. – Charles Aug 18 '16 at 04:48
  • 1
    [man strcat](http://linux.die.net/man/3/strcat) – Alex Aug 18 '16 at 05:02
  • @Charles, you should post a question about the `const_cast<>` trouble perhaps, using any `*printf %s` over `strcat` to do a string concatenation is kinda ugly (and perhaps a bit inefficient) (imho) – Alex Aug 18 '16 at 05:36
  • @Alex `strcat` gets more and more inefficient as the string gets longer, since it must scan the entire string to find the end. And it has no overrun protection. – user3386109 Aug 18 '16 at 05:40
  • @Charles there's no `const_cast<>` or any similar thing in C – phuclv Aug 18 '16 at 05:45
  • @user3386109 the _dst ptr_ can be incremented (as in your code) and a `strncat` variant exist (to be set with the remaining available buffer size), I'm not going to do it, but I think your answer can be easily modified to use `strncat`. Not here to impose anything, just saying. – Alex Aug 18 '16 at 05:53
  • @Alex Yup, but you would have to update the *dst ptr* by calling `strlen`. And if you are updating the *dst ptr* and calling `strlen`, then you may as well just use `memcpy` to move the bytes. I think the real advantage with using `snprintf` would be inside the `f(n,k)` function itself. Since the function will be doing numeric conversions anyways, it may as well use `snprintf` to do the conversions directly into the output string. – user3386109 Aug 18 '16 at 06:14

1 Answers1

5

Step 1 would be to allocate a big enough buffer for the string. Then use sprintf to print into the buffer. The standard idiom is

int index = sprintf( buffer, "some characters" );
for (; n >= 0; n--)
{
    index += sprintf( &buffer[index], ", %s", f(n, k) );
}
index += sprintf( &buffer[index], "more characters" );

The sprintf function returns the length of the string that it wrote, which allows you to keep track of where you are in the buffer.


You could also have the caller pass a buffer and size. In that case, it's best to use snprintf to avoid overrunning the buffer. So the function would look something like this

char *create_string( char *buffer, int size, int n, int k )
{
    ...
    index += snprintf( &buffer[index], size-index, ", %s", f(n, k) );
    ...
    return buffer;
}

where the size parameter is the size of the buffer.

user3386109
  • 34,287
  • 7
  • 49
  • 68
  • 1
    @chux Ok, I changed it from `length` to `size` to avoid confusion, and added some explanatory text. Thanks! – user3386109 Aug 18 '16 at 05:28
  • For your amusement only: here's how this translated into my code: https://github.com/CRGreathouse/PARI-extensions/blob/master/conv.gp.c#L99 – Charles Aug 19 '16 at 20:49