This is a badly designed warning from GCC.
When you apply the packed attribute to a structure, it makes the alignment requirement of its members one byte. Thus, when you take the address of a uint32_t member, what you get is not a uint32_t * but a uint32_t __attribute__ ((__aligned__(1))) *.1 It would be valid to assign this address to a uint32_t __attribute__ ((__aligned__(1))) * or (with a cast) to an unsigned char *.
Thus, ideally, the compiler would not warn you when you take the address of the member but rather when you use it in a way that requires greater alignment than it is guaranteed to have. Implementing such a warning may have been difficult at the time support for packed structures was created and this warning was added.
In this code:
st_t st;
st_t * st_p = &st;
st_p->b = 0;
the address of an st_t is taken and is assigned to an st_t *, so there is no problem. Then st_p->b accesses the member b but does not take its address, so there is no hazard that an unaligned address may be used as a pointer to an aligned address, so no warning is needed.
Footnote
1 GCC’s aligned attribute only allows increasing the alignment requirement of a type, not decreasing it. packed can decrease an alignment requirement, but GCC does not accept it on a plain uint32_t. So the __aligned__(1) notation is used in this answer to convey the intent of a uint32_t with a one-byte alignment requirement, in spite of the fact there appears to be no way to specify this in GCC.