1

I'm trying to write a recursive function for printing a tree-like structure. What I want to do is roughly as follows. Assume form is a function that takes the same arguments as printf, but returns the formatted string instead of printing it; Node is the struct that the structure is built from, with a val slot storing an unsigned long.

char* vals(Node* u) {
    if (leaf(u)) {
        return form("%lu", u->val);
    } else {
        return form("%s, %s", vals(u->left), vals(u->right);
    }
}

I'm not sure how to write this, as I've not been able to find a function similar to form.

Koz Ross
  • 3,040
  • 2
  • 24
  • 44
  • 2
    I'm not sure what you mean by “but returns the formatted string instead of printing it“… Are you looking for `sprintf`? – mafso Jun 27 '14 at 00:54
  • 2
    possible duplicate of [returning a local variable from function in C](http://stackoverflow.com/questions/4824342/returning-a-local-variable-from-function-in-c) – Kijewski Jun 27 '14 at 00:54
  • @mafso Yeah, it seems it was what I was looking for. Thanks. – Koz Ross Jun 27 '14 at 00:55
  • 1
    `sprintf`/`asprintf` is indeed the answer to the specific question you have posed. If your question is how to write something _like_ `sprintf`, you will have to `malloc` a block of memory to build the string into (and deal with the issues of `realloc`ing as necessary to make sure you never try to write past the end of that buffer), and you will have to manage pointers into that buffer to append to it appropriately. Using an off-the-shelf appendable-character-buffer library would handle those issues for you. – keshlam Jun 27 '14 at 01:00
  • @keshlam: Also, we need to remember that we have to deallocate the memory after it returns, or else we will have a significant amount of memory leaks. But it isn't something that cant be immediately solved. – ssm Jun 27 '14 at 02:06
  • @ssm: Granted; that might not be obvious to beginners. – keshlam Jun 27 '14 at 03:54

3 Answers3

3

As it happens, I have an implementation (two implementations, actually) of that function here at line 77 or so. It's just a simple wrapper designed to make calling the function easier; there are more efficient solutions but it doesn't usually make much difference.

/* Like GNU asprintf, but returns the resulting string buffer;
 * it is the responsibility of the caller to freee the buffer
 */
char* concatf(const char* fmt, ...);

The first one relies on vasprintf, which is a non-standard function which is part of Gnu glibc:

char* concatf(const char* fmt, ...) {
  va_list args;
  char* buf = NULL;
  va_start(args, fmt);
  int n = vasprintf(&buf, fmt, args);
  va_end(args);
  if (n < 0) { free(buf); buf = NULL; }
  return buf;
}

The second one relies on vsnprintf, which is a Posix-standard interface but was incorrectly implemented in some older standard C library implementations (glibc until version 2.0.6, for example.)

char* concatf(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  char* buf = NULL;
  int n = vsnprintf(NULL, 0, fmt, args);
  va_end(args);
  if (n >= 0) {
    va_start(args, fmt);
    buf = malloc(n+1);
    if (buf) vsnprintf(buf, n+1, fmt, args);
    va_end(args);
  }
  return buf;
}

Feel free to use them as you need them. They're not particularly profound.

For gcc (and, I think, clang), you can declare the function as:

char* concatf(const char *fmt, ...)
      __attribute__ ((format (printf, 1, 2)));

which will enable the compiler to check the validity of the format string, as it does with printf

rici
  • 234,347
  • 28
  • 237
  • 341
1

You're probably looking for asprintf? Note that you should also free previously received allocated strings once you have used them and they are no longer needed (i.e. between the asprintf that uses them and the return, so you'll need an intermediate variable).

jcaron
  • 17,302
  • 6
  • 32
  • 46
0

I would suggest creating two form functions, one for the leaf nodes and the other for the non-leaf nodes.

char* formLeaf(Node* u) {
    char leafRet[100];
    sprintf(leafRet,"%lu", u->val); 
    return strdup(leafRet);
}

char* vals(Node* u);

char* formNonLeaf(Node* u) {
    char* left = vals(u->left);
    char* right = vals(u->left);
    char* ret = malloc(strlen(left) + strlen(right) + 3);
    sprintf(ret, "%s, %s", left, right);
    free(left);
    free(right);
    return ret;
}

char* vals(Node* u) {
    if (leaf(u)) {
        return formLeaf(u);
    } else {
        return formNonLeaf(u);
    }
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270