4

Lets imagine that we have function that should return two return values. For instance we have some function that returns char* and its length. Char is allocated within that particular function.

I can imagine following ways of doing that:

int foo(char **result);       // Passing pointer to char*, returning int
char* bar(int *len);          // Passing pointer to int, returning char*
struct char_and_len foobar(); // Returning struct that contains both values

Is there any other ways to implement multiple values and what's the most effective way to do that?

I'd really appreciate detailed explanation, considering performance, memory alignment or any other hidden C feature.

2
  • 1
    Why get any more clever than the options you've provided? You'll only be obfuscating your code by doing so. Commented Jan 3, 2014 at 20:16
  • @meagar. Why? I'm curious what's the other ways of doing that. There might be some really interesting reasons of doing that. Moreover, I have strong feeling that in C different ways of doing the same thing might have different effects and considerations. Commented Jan 3, 2014 at 20:19

7 Answers 7

3

There are two cases here. If you are working in a codebase/framework (e.g. Glib) that has a standard string struct used throughout the entire application, then use your third option:

struct string function();

If your codebase is not using one standard struct everywhere for strings, I would advise against using a struct. The hassle of converting back and forth is not really worth it.

Otherwise, the convention (at least that I've seen) is to return the char* and have the length as a pointer parameter:

char* function(int* length);
Sign up to request clarification or add additional context in comments.

3 Comments

Could you please elaborate why char* f(int*) vs int f(char **)
@Sigurd: At least in most of the code and library functions I've seen, char** is used when the caller is responsible for memory allocation and has already determined the length, or is providing a maximum length. For example, in the Linux system call read(2), you provide a preallocated buffer and a maximum data length for that buffer.
"Otherwise, the convention is to return the char* and have the length as a pointer parameter:" -- asserting that the convention is this is a tad bit harsh. There are other, equally valid conventions as well.
1

Another way:

void foo(char **result, int *len);

The worst in my opinion is:

struct char_and_len foobar();

The one I prefer is the one I showed you, because I don't like to mix return values in both arguments and effective return.

Comments

1

You can just return an array and wrap it in a struct:

typedef struct {
    char *strings[2];
} RetType;

RetType func()
{
    return (RetType){ { "foo", "bar" } };
}

Another idiomatic solution is C to pass an array to your function and have it filled:

void func(char *strings[2])
{
    strings[0] = "foo";
    strings[1] = "bar";
}

A third solution is to return one value and pass the other one by pointer (though this is more meaningful if you have values of distinct types):

char *func(char **outparm)
{
    *outparm = "foo";
    return "bar";
}

Also, excuse me for not being const-correct.

4 Comments

I might be not too clear in my question. I need to return int and char *. Let's imagine that char is not terminated with \0 so we do have to return its length.
@Sigurd Then you can change your struct to contain a char * and an int. And you can change one of the return values in the third example to an int too. And forget about the second approach.
I know how to return multiple values :) My question was what's the most effective way and why.
@Sigurd Effective in what sense? I've listed all the practically possible solutions. Pick one. It really doesn't matter.
1

My favourite would be

void foobar(struct char_and_len*);

For the following reasons:

  • Only a single parameter needs to be passed
  • return value / out parameter aren't mixed
  • return values can be ignored, especially when the return value needs to be deallocated again this can be a serious source of programming errors. Out parameters cannot be ignored.
  • Having a pointer to the struct avoids too much copy operations. Only one pointer needs to be provided to the function.
  • This way the caller of the function can decide where the struct char_and_len is stored (on the heap, stack) while when using return values the data needs to be put on the stack at least temporarily

2 Comments

If you have a struct made for this purpose why not return the whole struct by value.
While in general I would agree with you, because we're dealing with strings here don't you think it's a problem to be defining a string "type" that has to be converted to and from?
1

Use a string.

For the specific example you cite, remember that length is implicitly or explicitly a property of any string type. For example, C-style strings are null-terminated, so even though there's not an explicit length the caller can still determine the length of the string. Pascal-style strings include length as the first byte. char* isn't necessarily a string, it might be a plain old text buffer where you do need the length. But the point of string types is to avoid the need to pass data and length separately.

More generally...

A function can only return one value, so if you need to return more than that you need to either package everything up into a single value using a struct, or pass a pointer (or pointers) to a location to receive the results. Which method you use depends on the circumstances. Do you already have a struct defined for the data that needs to be returned? Is the caller likely to have an existing object that could receive the results? There is no best method, only an appropriate method for the situation.

Comments

0

The best way is always work with pointers. In C, every function duplicate the arguments passed in a different zone of memory. The best way is the second one of the three that you have posted in term of performance.

But I would pass as argument a pointer to a struct wich contains both, lenght and string with a void return.

4 Comments

Strings in C are always pointers, no matter whether you pass the pointer itself by reference or value.
Yep, but he has an int too.
An int is typically four bytes. On a 32-bit system, so is a pointer. On a 64-bit system, the pointer (8 bytes) will actually be larger than the int.
On a 64-bit system int and *int have the same size, 4 bytes not 8. I have just checked that (On 32-bit system it is 2 bytes). So in that case you are not going to notice any improvement on performance :P For that reason I said that I would use a single pointer to a struct with both data type in it.
0

In C it isn't "normal" to return two values. That means that as you already did you could create a struct to return the result. That is the formally correct way of doing it, but not the simplest one and it is coumbersome. So I would totaly forget that solution.

Generically, the other way of returning results is by passing arguments by reference (in Pascal VB etc ). In C there is not the possibility to pass them by reference instead a pointer is passed. But in C++ there is the possibility to pass the variable by reference ( that simply means passing the pointer but using the variable ).

So I believe the simplest way of doing what you need is to define:

char* bar(int *lenP); //

now you have the result of a function that could return two results:

like if it was defined as pseudo syntax (s,l)=bar();

Additionally you could also use:

void bar(char * *s,int * lenP); // that case treats equally the too arguments.

In C++ I would use the by reference approach because while being the same from the practical point of view (what the processor does) it is simpler for the programmer.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.