There is one global variable, x. In the activation record for the function main there is a local variable, y. In the activation record for f there is one parameter variable, z, and one local variable, x. The parameter variable z is just like a local variable, except that it gets its initial value from the argument in the function call.
(C calls normal "local" variables "variables with automatic storage", and it calls "global" variables "variables with external storage".)
Note that a local name hides a global name, so in the assignment of y in main the variable that is assigned is the local y. There is no local x in main, so in the assignment of x in main it is the global x that is assigned.
In f there is a local x, so the assignment of x is an assignment to the local variable.
There are three global variables: a, b and p. In the activation record for the function main there are three local variables: k, m and a. The value of m in main is undefined, which usually means that it contains whatever data that happened to have been in that memory location. In the activation record for g there is one parameter variable, h, and three local variables, i, p and a. In the activation record for f there are two parameter variables, c and d, and two (normal) local variables, b and p. Four integer-sized spaces have been allocated on the heap.
b) We call the directly reachable variables (the global variables and the variables on the stack) the root set. From the image in the solution above to question 2, we see that the root set contains three pointer variables, all called p. The memory areas on the heap that are pointed to by those three variables contain the integer values 10, 11 and 18. Those memory areas are still reachable from the root set, and may be used later in the program. They are not garbage. The memory area on the heap that contains 14 is not reachable from the root set. Therefore it can never be accessed again. It is garbage, and can be reclaimed.
The automatic garbage collector must have some way to go through all allocated memory areas. The heap management system (the system that handles calls to malloc and free) will have some sort of table or list of those areas, and the garbage collector could use that table or list. Then the garbage collector starts with the root set, and it finds which areas on the heap are reachable from pointers in those variables, possible indirectly through other memory areas. Areas on the heap that are not reachable from the root set can't be reached from the program, and since they can't be reached, they can't ever again be used by the program, so they can be safely deallocated.
c) Now the stack frames for the functions f and g have been deallocated, so they are no longer part of the root set. The only pointer variable in the root set is the global p, pointing to the area on the heap that contains the integer value 10. All other areas on the heap can now be freed.
b)
The program continues to recursively call the function g. The figure shows the stack after two more calls to g. Execution will continue until the stack is full, and the program (probably) crashes. (C calls this "undefined behavior". Anything can happen.)
c) Yes. The recursive call to g in g is an example of tail recursion. A compiler that performs elimination of tail recursion, which gcc does with the flag -O2 or -O3, will remove the recursive call, and not allocate a new activation record on the stack. Since no more work needs to be done in the current activation of g, it can be re-used for the next activation of g, so there will only be a single activation record for g on the stack.
The reference variable h in main is not a pointer, but an alias for the variable c. The reference parameter r in f is an alias for b in main, and the reference parameter rp in f is an alias for e in main.