0

Just trying to make a simple toString function but I'm having difficulty figuring out how I can create an sprintf style string without having to create a temporary variable.

i.e. It would be wonderful if the following worked

#myfile.c
bool status;
int state;
...
const char* myfileToString(void) {
    return sprintf("myfile::status=%s;state=%d;", \
                   status ? "true" : "false", \
                   state);
}

this won't work since sprintf needs you to first create the string then pass it to the function.

i.e. I believe the following works

#myfile.c
bool status;
int state;
...
const char* myfileToString(void) {
    char ret[40];
    sprintf(ret, \
            "myfile::status=%s;state=%d;", \
            status ? "true" : "false", \
            state);
    return ret;
}

is there another function that I can use instead of sprintf or can I do something weird with sprintf like passing it some pointer to return directly sprintf(*return, "...", ...);

2
  • 1
    Note that the second example does not work because you are returning a pointer to a local variable which goes out of scope when you return from the function. Commented Jul 29, 2014 at 15:35
  • 1
    Take a look at asprintf, there are portable sources for it if your platform does not provide it. Commented Jul 29, 2014 at 15:35

6 Answers 6

7

Okay...

Part 1:

  • Unless you're writing a macro, you don't need the backslash at the end of unfinished lines.
  • sprintf() requires a mutable buffer as it's first argument. You're not passing one, so you'll get all sorts of bad behavior here.

Part 2:

  • You're returning a pointer to a stack allocated object. That's going to cause undefined behavior.

What you should do:

Allocate the memory outside of this function. So:

void myfileToString(const char *str, size_t len) {
    snprintf(str, len, "myfile::status=%s;state=%d;",
        status ? "true" : "false",
        state);
    str[len - 1] = '\0';
}

You would call this by doing:

char box[40];
myfileToString(box, sizeof(box) /* 40 */);

Making this more reliable:

The family of sprintf() functions returns the number of characters written to a buffer. You could use this to send back an error (say a true or false return value) if the number of characters that was written was greater than the size of the buffer.

Finally, you could use the heap:

It's not the greatest practice, because it can mess with what people expect. But you could allocate memory on the heap for this data. You would need to remember to call free() on it in the future.

const char *myFileToString() {
    char *str = malloc(40); 
    snprintf(str, 40, "myfile::status=%s;state=%d;",
        status ? "true" : "false",
        state);
    str[40 - 1] = '\0';
}
Sign up to request clarification or add additional context in comments.

1 Comment

I was really hoping I could get away from predefining the char array. I also try to use malloc as little as possible. I guess my goal of printf(myfileToString()); or char* myfilestr = myfileToString() is an impossible one (without malloc)? I don't really like forcing the developer to know what size the return string should be. I think I'll go with your What you should do though. What about other forms of string concatenation?
1

In the first version you return what sprintf returns, which won't work as it doesn't actually return a string (please read the linked reference). In the second version of the function you return a pointer to a local variable, this leads to undefined behavior as the local variable goes out of scope when the function returns and the pointer no longer points to valid memory.

There are two primary solutions:

  1. Allocate the string dynamically, e.g. using malloc, and return that pointer. Major drawback is that you must free the pointer you return.

  2. Pass a character array as argument to the function, and use that as destination for the string.

Also, you should rather use snprintf than plain sprintf, because then you can control how much is written to the destination string, so you don't get a buffer overflow. On Windows using the Visual C++ compiler it's called _snprintf.

2 Comments

Mention GNU asprintf, and availability of free implementations for adding it where not provided?
Nice detailed answer.
1

You don't want to use a local variable to build your sting in. Once you return, the variable will be lost and your return value will be undefined. Instead you can either allocate memory in your routine with malloc(), or you can pass a pointer to preallocated memory into your routine.

In either case you ought to use snprintf() instead of sprintf() to make sure you don't overflow your buffer.

Comments

0

You can't return a char[] in a function because it is allocated statically (on your stack) and destroyed at the end of your function. For you toString function, you have to use malloc in order to return a char* allocated dynamically (in your heap)

Comments

0

Your first method is totally wrong! You should not use like that! Instead of sprintf you can use snprintf

snprintf(ret,sizeof(ret),"myfile::status=%s;state=%d;",status ? "true" : "false",state);

1 Comment

Your first method is totally wrong I knew that when I wrote it. That's why I wrote this won't work since sprintf needs you to first create the string then pass it to the function. I was hoping someone knew of an alternate function in one of the standard libraries that worked the way I wrote that.
0

I use this (very nasty) trick:

  struct strang
  {
    char  s[100] ;     /* this is arbitrary, but has to be enough ! */
  } ;

and, by way of an example, to convert a struct sockaddr to a suitable string:

  struct strang
  sockaddr2strang(struct sockaddr* sa)
  {
    struct strang str ;
    struct sockaddr_in* sin ;
    ....

    switch (sa->sa_family)
    {
       AF_INET:
         sin = (struct sock_addr_in*)sa ;
         inet_ntop(AF_INET, &sin->sin_addr, str.s, sizeof(str)) ;
         break ;
       ....
     } ;

    return str ;
  } ;

The cunning part is that you can use the result in an expression or as the argument for another function, for example:

  printf("....%s..%s..", ...., sockaddr2strang(&sa_local).s, 
                                       ...., sockaddr2strang(&sa_peer).s) ;

Note that this is entirely pthread-safe, and does not require the caller to set up and pass in any auxiliary buffer(s) -- in particular (as shown here) if more than one string is required at the same time, the caller doesn't have to manage two (or more) buffers etc.

Obviously, this will not work for return strings of arbitrary size.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.