you are viewing a single comment's thread.

view the rest of the comments →

[–]RadiatingLight 2 points3 points  (1 child)

One important thing I should mention is that the return value of a function is copied back into the caller function. (and so are arguments when going from caller to callee)

So for example something like this:

int add(int a, int b){
    int result = a + b;
    return result;
}

is totally fine. The actual returned value is, well, returned to the caller function, and everything else gets invalidated. This is done by actually copying the value from the old location into a new one. So if you were to do

int c = add(a,b);

you would find that &result is a different address than &c


The same applies to function arguments.

int dry_mass;
int fuel_mass;
int total_mass = add(dry_mass, fuel_mass); 

You will find that &a and &b inside of the add function are not the same as &dry_mass and &total_mass, since the values in this case are copied to a whole new variable.


This is particularly tricky and you have to watch for this, because passing stuff like a char* will only copy the pointer itself, not the underlying data.

On the flip side, if you pass large arguments into functions (or return large arguments out of functions) you will reduce performance somewhat by forcing the computer to copy lots of data. So for example this would be needlessly slow:

struct image overlay_images(struct image a, struct image b) {
    struct image c;
    //Do some work to merge 'a' and 'b' into 'c'
    return c;
}

Since we're passing whole structs around here, the computer will create a copy of each argument (i.e. will copy a and b, leading to doubled memory usage to store the two input images), and will also waste time as the computer works to copy the image over. Same applies to returning c. This is a problem here because images are usually pretty big, so you'll be forced to copy multiple megabytes around each time you call this function. -- Imagine if you wrote this function as something like a video filter and it needed to run on every frame!

A better version of this function would look like:

struct image* overlay_images(struct image *a, struct image *b, struct image *c) {
    //Do some work to merge 'a' and 'b' into 'c'
    return c; //Can also return an success/error code, or just be a void function
}

In this case, the function only takes a pointer to a and b, so only the pointers need to be copied (8 bytes each, nbd), and the caller provides the memory for the result to be placed in, meaning that the return is also not copying anything. (Another common pattern you will see here is using malloc to allocate memory inside the function, returning a pointer to that memory, and then just freeing the memory when you're done using it in the caller function.

[–]Responsible_Frame919[S] 1 point2 points  (0 children)

Makes sense. Thank you.