9

In my C programs I often need a way to make a string representation of my ADTs. Even if I don't need to print the string to screen in any way, it is neat to have such method for debugging. So this kind of function often comes up.

char * mytype_to_string( const mytype_t *t );

I actually realize that I have (at least) three options here for handling the memory for the string to return.

Alternative 1: Storing the return string in a static char array in the function. I don't need much thinking, except that the string is overwritten at every call. Which may be a problem in some occasions.

Alternative 2: Allocate the string on the heap with malloc inside the function. Really neat since I then won't need to think of the size of a buffer or the overwriting. However, I do have to remember to free() the string when done, and then I also need to assign to a temporary variable such that I can free. and then heap allocation is really much slower than stack allocation, therefore be a bottleneck if this is repeated in a loop.

Alternative 3: Pass in pointer to a buffer, and let the caller allocate that buffer. Like:

char * mytype_to_string( const mytype_t *mt, char *buf, size_t buflen ); 

This brings more effort to the caller. I also notice that this alternative gives me an other option on the order of the arguments. Which argument should I have first and last? (actually six possibilities)

So, which should I prefer? Any why? Is there some kind of unwritten standard among C developers?

  • 2
    Just an observational note, most operating systems use option 3 - caller allocates buffer anyhow; tells the buffer pointer and capacity; callee fills buffer and also returns the actual length of string if the buffer is insufficient. Example: sysctlbyname in OS X and iOS – rwong Feb 8 '15 at 0:30
9

The methods I've seen most are 2 and 3.

The user supplied buffer is actually quite simple to use:

char[128] buffer;
mytype_to_string(mt, buffer, 128);

Though most implementations will return the amount of buffer used.

Option 2 will be slower and is dangerous when using dynamically linked libraries where they may use different runtimes (and different heaps). So you can't free what has been malloced in another library. This then requires a free_string(char*) function to deal with it.

  • Thanks! I think I like Alternative 3 best as well. However I want to be able to do things like: printf("MyType: %s\n", mytype_to_string( mt, buf, sizeof(buf)); and hence I won't like to return the length used but rather the pointer to the string. The dynamic library comment is really important. – Øystein Schønning-Johansen Feb 8 '15 at 10:34
  • Shouldn't this be sizeof(buffer) - 1 to satisfy the \0 terminator? – Michael-O Nov 9 '16 at 12:27
  • @Michael-O no the null term is included in the buffer size meaning that the max string that can be put in is 1 less than the passed in size. This is the pattern that the safe string functions in the standard library like snprintf use. – ratchet freak Nov 9 '16 at 12:51
  • @ratchetfreak Thanks for clarification. Would be nice to extend the answer with that wisdom. – Michael-O Nov 9 '16 at 13:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.