We print address of dynamically allocated array in function and in main to be sure it points to the same place and use it to show it is usable. We must use pointer to integer in main because expression
int i = allocator(2);
is illegal initializer. It doesn't prevent us from using pointer to integer later as array. Finally we free allocated memory. That what we did is called manual memory allocation and such memory is placed on heap. If we want to use pointer arithmetic we will do something like this:
*i = 1;
*(i + 1) = 2;
printf ("%d, %d\n", *i, *(i+1));
Please note that *(i + 1) is not the same as *i + 1 even if it produces the same output in our example. Dereferencing pointer got higher order of precedence than addition. Because of precedence we are using braces. What if we need two-dimensional array? Two-dimensional array is array of arrays and we will need pointer to pointer.
As we see allocating is more complicated and feeing also. It goes in two steps, first we allocate array of rows and after that we allocate arrays of columns. As in previous case we can use array syntax. Now we can go mixing pointer arithmetic and array syntax or go completely pointer arithmetic
Maybe to add that *(*i+1) corresponds to i. That was how to do it, now how not to do it. Going heroic and forcing through pointer to array as return type.
This compiles and executes with few warnings, if we change fun return type by adding another pointer int* (*fun()), that also works. If something looks difficult, like returning address of array, than design needs additional work. For example
this is much nicer and easy to understand. Please note that returning static array is perfectly fine and that we do not use free on it. Statically declared variables are allocated automatically and available through execution of program. If we forget to declare array as static than we will return dangling pointer, local variable goes out of scope and returned pointer points to who knows what.