开发者

How to return an integer from a function

Which is considered better style?

int set_int (int *source) {
   *source = 5;
   return 0;
}

int main(){
   int x;
   set_int (&x);
}

OR

int *set_int (void) {开发者_JS百科
    int *temp = NULL;
    temp = malloc(sizeof (int));
    *temp = 5;
    return temp;
}

int main (void) {
    int *x = set_int ();
}

Coming for a higher level programming background I gotta say I like the second version more. Any, tips would be very helpful. Still learning C.


Neither.

// "best" style for a function which sets an integer taken by pointer
void set_int(int *p) { *p = 5; }

int i;
set_int(&i);

Or:

// then again, minimise indirection
int an_interesting_int() { return 5; /* well, in real life more work */ }
int i = an_interesting_int();

Just because higher-level programming languages do a lot of allocation under the covers, does not mean that your C code will become easier to write/read/debug if you keep adding more unnecessary allocation :-)

If you do actually need an int allocated with malloc, and to use a pointer to that int, then I'd go with the first one (but bugfixed):

void set_int(int *p) { *p = 5; }

int *x = malloc(sizeof(*x));
if (x == 0) { do something about the error }
set_int(x);

Note that the function set_int is the same either way. It doesn't care where the integer it's setting came from, whether it's on the stack or the heap, who owns it, whether it has existed for a long time or whether it's brand new. So it's flexible. If you then want to also write a function which does two things (allocates something and sets the value) then of course you can, using set_int as a building block, perhaps like this:

int *allocate_and_set_int() {
    int *x = malloc(sizeof(*x));
    if (x != 0) set_int(x);
    return x;
}

In the context of a real app, you can probably think of a better name than allocate_and_set_int...


Some errors:

int main(){
 int x*;  //should be int* x; or int *x;
 set_int(x);
}

Also, you are not allocating any memory in the first code example.

int *x = malloc(sizeof(int));

About the style:
I prefer the first one, because you have less chances of not freeing the memory held by the pointer.


The first one is incorrect (apart from the syntax error) - you're passing an uninitialised pointer to set_int(). The correct call would be:

int main()
{
    int x;
    set_int(&x);
}

If they're just ints, and it can't fail, then the usual answer would be "neither" - you would usually write that like:

int get_int(void)
{
    return 5;
}

int main()
{
    int x;
    x = get_int();
}

If, however, it's a more complicated aggregate type, then the second version is quite common:

struct somestruct *new_somestruct(int p1, const char *p2)
{
    struct somestruct *s = malloc(sizeof *s);

    if (s)
    {
        s->x = 0;
        s->j = p1;
        s->abc = p2;
    }

    return s;
}

int main()
{
    struct somestruct *foo = new_somestruct(10, "Phil Collins");

    free(foo);

    return 0;
}

This allows struct somestruct * to be an "opaque pointer", where the complete definition of type struct somestruct isn't known to the calling code. The standard library uses this convention - for example, FILE *.


Definitely go with the first version. Notice that this allowed you to omit a dynamic memory allocation, which is SLOW, and may be a source of bugs, if you forget to later free that memory.

Also, if you decide for some reason to use the second style, notice that you don't need to initialize the pointer to NULL. This value will either way be overwritten by whatever malloc() returns. And if you're out of memory, malloc() will return NULL by itself, without your help :-). So int *temp = malloc(sizeof(int)); is sufficient.


Memory managing rules usually state that the allocator of a memory block should also deallocate it. This is impossible when you return allocated memory. Therefore, the second should be better.

For a more complex type like a struct, you'll usually end up with a function to initialize it and maybe a function to dispose of it. Allocation and deallocate should be done separately, by you.

C gives you the freedom to allocate memory dynamically or statically, and having a function work only with one of the two modes (which would be the case if you had a function that returned dynamically allocated memory) limits you.

typedef struct
{
    int x;
    float y;
} foo;

void foo_init(foo* object, int x, float y)
{
    object->x = x;
    object->y = y;
}

int main()
{
    foo myFoo;
    foo_init(&foo, 1, 3.1416);
}


In the second one you would need a pointer to a pointer for it to work, and in the first you are not using the return value, though you should.

I tend to prefer the first one, in C, but that depends on what you are actually doing, as I doubt you are doing something this simple.

Keep your code as simple as you need to get it done, the KISS principle is still valid.


It is best not to return a piece of allocated memory from a function if somebody does not know how it works they might not deallocate the memory. The memory deallocation should be the responsibility of the code allocating the memory.


The first is preferred (assuming the simple syntax bugs are fixed) because it is how you simulate an Out Parameter. However, it's only usable where the caller can arrange for all the space to be allocated to write the value into before the call; when the caller lacks that information, you've got to return a pointer to memory (maybe malloced, maybe from a pool, etc.)


What you are asking more generally is how to return values from a function. It's a great question because it's so hard to get right. What you can learn are some rules of thumb that will stop you making horrid code. Then, read good code until you internalize the different patterns.

Here is my advice:

  • In general any function that returns a new value should do so via its return statement. This applies for structures, obviously, but also arrays, strings, and integers. Since integers are simple types (they fit into one machine word) you can pass them around directly, not with pointers.

  • Never pass pointers to integers, it's an anti-pattern. Always pass integers by value.

  • Learn to group functions by type so that you don't have to learn (or explain) every case separately. A good model is a simple OO one: a _new function that creates an opaque struct and returns a pointer to it; a set of functions that take the pointer to that struct and do stuff with it (set properties, do work); a set of functions that return properties of that struct; a destructor that takes a pointer to the struct and frees it. Hey presto, C becomes much nicer like this.

  • When you do modify arguments (only structs or arrays), stick to conventions, e.g. stdc libraries always copy from right to left; the OO model I explained would always put the structure pointer first.

  • Avoid modifying more than one argument in one function. Otherwise you get complex interfaces you can't remember and you eventually get wrong.

  • Return 0 for success, -1 for errors, when the function does something which might go wrong. In some cases you may have to return -1 for errors, 0 or greater for success.

The standard POSIX APIs are a good template but don't use any kind of class pattern.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜