Search code examples
arrayscfunctional-programmingstateside-effects

Avoiding Side Effects in C


This question particularly applies to arrays.

In many languages, I would do something like this:

    #This is in python for simplicity:

    def increment(mylist):
        for i in mylist:
            i += 1;
            
        return mylist
    

    mylist = {0,1,2,3}
    mylist = increment(mylist)

I tried several ways to return arrays in C, none of which worked as above. It seems that C simply was not intended to work this way. Instead, I had to do this:

#include <stdio.h>

increment(int *myarray, int size) {
    for(int i = 0; i < size; i++){
        myarray[i] += 1;
        
    }
}

int main(){
    int myarray[4] = {0,1,2,3};
    increment(myarray, 4);

}

Needless to say, the C function changes the state of the array and is therefore a side effect function. There are good reasons to avoid this (which are not the topic of this question).

Is there a way to avoid these types of side effects in C?


Solution

  • Changing the contents of an array is always a "side-effect" in C, as the formal definition goes. If you are rather looking for a way to make an array etc immutable, as in read-only and always creating a new object upon manipulation, there are ways to do that too.

    You have to be aware that this typically involves a "hard copy" of the data contents, so it comes with execution overhead. C gives you the option not to be that inefficient if you don't want to. But if you want it, then the more flexible option is dynamic allocation. Something like this:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int* increment(const int* myarray, int size) 
    {
      int* new_obj = malloc(sizeof(int[size]));
      for(int i=0; i<size; i++)
      {
        new_obj[i] = myarray[i] + 1;
      }
      return new_obj;
    }
    
    int main (void)
    {
      int* myarray = malloc(sizeof(int[4]));
      memcpy(myarray, (int[]){0,1,2,3}, sizeof(int[4]));
      for(int i=0; i<4; i++)
      {
        printf("%d ", myarray[i]);
      }
      puts("");
      
      int* another_array = increment(myarray, 4);
      free(myarray);
      
      for(int i=0; i<4; i++)
      {
        printf("%d ", another_array[i]);
      }
    
      free(another_array);
    }
    

    Note that this is significantly slower than modifying the original array in place. The heap allocation and the data copy are both relatively slow.

    You could create "bad API" functions in C though, such as

    int* increment(int *myarray, int size) {
        for(int i = 0; i < size; i++){
            myarray[i] += 1;
            
        }
        return myarray;
    }
    

    This returns a pointer to the same array that was passed along. It's bad API because it's confusing, though some C standard functions were designed just like this (strcpy etc). And in order to use this function you need a pointer to the first element of the array, rather than the array itself.