Your code correctly:
#include <stdio.h>
int main()
{
int c = 12;
int * pc = &c;
int ** ppc = &pc;
printf("pc = %p, the value pc points to = %d\n", pc, *pc);
printf("ppc = %p, the value ppc points to = %d\n", ppc, **ppc);
return 0;
}
In the first printf() call notice that I changed the first format to %p, that is, to print the value as a memory address and I left the second format to be %d, that is, to print the value (int). Accordingly, the first argument after the format string is pc, which is a pointer to int (int *). A memory address. The second argument is the value of pc (*pc) using the dereference operator to get the value where the memory address points to, that is, to the int value. This is the value stored in c.
Similarly, in the second printf(), first we want a memory address to be printed (ppc), and after that, we want to dereference twice as ppc is a pointer that points to another pointer that points to a number (int**). So **ppc has the same value as c.
I hope this helps.