I first create a char pointer assign an integer address
No, you create a pointer-to-char and assign the address of an integer to it.
An integer address isn't a thing, and integers don't live in some magically different address space to characters.
An pointer-to-integer is an address (which is essentially typeless and could point to any data type) coupled to a type (the thing stored at this address is an integer).
When you cast your int* to a char*, the address is unchanged. You're just choosing to lie to the compiler about the type stored at that address, for reasons best known to yourself.
My guess is that (int *)po casts po to integer type
When you cast po back to int*, the address is still unchanged, and it's still the address of your original integer. You just admitted to the compiler that it isn't "really" a char stored there.
Casting to "integer type" would mean (int)po, which isn't what you did. You seem to be confusing the type of the pointer with the type of the thing it points at.
then *(int *)po retrieves the value pointed by this integer type pointer. Not sure though.
Yes, that's correct. It's just the same as dereferencing any other pointer-to-integer, you get the value of the integer it points to. You could trivially split the expression up as
int *pi = (int*)po;
int i = *pi;
and then print that. You can also print the address of a pointer with to confirm everything is what you expect (or just inspect these values in a debugger)
char *po;
int *pi;
int i;
int y=9999;
po = (char *)&y;
pi = (int *)po;
i = *pi;
printf("y=%d\n &y=%p\n po=%p\n pi=%p\n i=%d\n &i=%p\n",
y, (void*)&y, (void*)po, (void*)pi, i, (void*)&i);
What if ... y was some struct with multiple different members ...
You're just asking in general what happens when you cast a pointer-to-X to a pointer-to-Y and back to pointer-to-X?
It's fine. You're just telling stories about the pointed-to type, but the address never changes.
If you want to access your X through a pointer-to-Y, you need to read the strict aliasing rules