See the Go blog article. He explains in detail what is happening and fully answers your question.
In the section Transfer slices in functions :
It is important to understand that although a slice contains a pointer, it is itself a value. Under covers is a structure value containing a pointer and a length. This is not a pointer to a structure.
As a result, you need a pointer receiver, or you need to return the slice as a value if you want to change it using append .
If you just want to change the contents of the fragment, you can simply pass the slice by value:
Despite the fact that the slice header is passed by value, the header includes a pointer to the elements of the array, so both the original slice header and a copy of the header passed to the function describe the same array. Therefore, when a function returns, modified elements can be visible through the original slice variable.
With append you change the slice title. AND
Thus, if we want to write a function that changes the title, we must return it as a result parameter
Or:
Another way to have the function to change the slice header is to pass a pointer to it.
You also have confusion about using pointers. See spec :
For operand x of type T, the operation address & x generates a pointer of type * T to x.
and
For operand x of type pointer * T, pointer-pointer * x denotes a variable of type T pointed to by x.
So your example *stack = append(*stack, x) does not mean that you are passing a pointer to append , quite the opposite - you are casting a pointer to pass the value that it points to.