8

What is considered better practice when writing methods that return strings in C?

passing in a buffer and size:

void example_m_a(type_a a,char * buff,size_t buff_size)

or making and returning a string of proper size:

char * example_m_b(type_a a)

P.S. what do you think about returning the buffer ptr to allow assignment style and nested function calls i.e.

char * example_m_a(type_a a,char * buff,size_t buff_size)
{
...
return buff;
}

5 Answers 5

5

Passing a buffer as an argument solves most the problems this type of code can run into.

If it returns a pointer to a buffer, then you need to decide how it is allocated and if the caller is responsible for freeing it. The function could return a static pointer that doesn't need to be freed, but then it isn't thread safe.

Sign up to request clarification or add additional context in comments.

2 Comments

I guess it just feels a bit ugly to me compared to the way you do things in other languages.
@Roman: Probably because many modern languages have a true string type, whereas C does not. In C, you have to decide where the buffer comes from. If the caller provides it, then at least it's easy for the caller to know where the memory came from.
3

Passing a buffer and a size is generally less error-prone, especially if the sizes of your strings are typically of a "reasonable" size. If you dynamically allocate memory and return a pointer, the caller is responsible for freeing the memory (and must remember to use the corresponding free function for the memory depending on how the function allocated it).

If you examine large C APIs such as Win32, you will find that virtually all functions that return strings use the first form where the caller passes a buffer and a size. Only in limited circumstances might you find the second form where the function allocates the return value (I can't think of any at the moment).

Comments

1

I'd prefer the second option because it allows the function to decide how big a buffer is needed. Often the caller is not in a position to take that decision.

Comments

1

Another alternative to the pass a buffer and size style, using a return code:

size_t example_m_a(type_a a,char * buff,size_t buff_size)

A zero return code indicates that the caller's buffer was suitable and has been filled in.

A return code > 0 indicates that the caller's buffer was too small and reveals the size that is actually needed, allowing the caller to resize his buffer and retry.

2 Comments

To me that might be a bit confusing as some apis return the size of the string on success or 0/-1 on fail
Agree that it goes against the norm of many API, but as long as it's documented as such I don't see a problem with it. Of course, use other flavors to suit your taste like using a third argument of size_t *size_needed instead of using the return code. My main point was the idea of providing the flexibility of giving the caller an option on failure (re: David Heffernan's answer).
0

Passing buffer address and length is best in most cases. It is less error-prone and one does not have to worry about memory leaks. In fact, in some tight embedded systems it is completely undesirable to use the heap. However, the function must not overrun the buffer as that can crash the system and worse: make it vulnerable to hackers.

The only time where I've seen function returning allocated buffer is libxml's API to generate XML text from xmlDoc.

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.