char *s_gets(char * st, int n);
returns a pointer to char, while
char s_gets(char * st, int n);
returns a plain char.
I would like to ask also why is there a pointer in the function declaration in the first place? What is its purpose?
It indicates that the function returns a pointer value.
C declaration syntax uses something called a declarator to convey the pointer-ness, array-ness, function-ness, or combination thereof of an item. For example, take the declaration
int *p;
The type of p is int * (pointer to int). The type is specified by the combination of the type specifier int and the declarator *p. The int-ness of p is given by the type specifier int and the pointer-ness is given by the declarator *p.
If you substitute p with f(void), you get
int *f(void);
The type of f is "function returning pointer to int". The pointer-ness and function-ness of f are specified with the declarator *f(void).
For any type T and any declarator D, the following are true:
T D; // D is an instance of T
T D[N]; // D is an array of T
T D(); // D is a function returning T
T *D; // D is a *pointer* to T
T *D[N]; // D is an array of pointers to T
T *D(); // D is a function returning a pointer to T
T (*D)[N]; // D is a pointer to an array of T
T (*D)(); // D is a pointer to a function returning T
In both declarations and expressions, the postfix [] subscript and () function-call operators have higher precedence than unary *, so *a[N] will always be parsed as *(a[N]) (array of pointers). To declare something as a pointer to an array or a pointer to a function, you need to explicitly group the * operator with the declarator using parentheses.
Declarators can get arbitrarily complex:
T (*D())[N]; // D is a function returning a pointer to an array of T
T (*D[N])(); // D is an array of pointers to functions returning T
T *(*(*D)[N])[M]; // D is a pointer to an array of pointers to an array of pointers to T