int *aptr = 21; does not store 21 in *aptr. When = is used in a declaration, it sets the initial value for the thing being declared (which is aptr), not for the “expression picture” used for the declaration (*aptr).
In C declarations, we use pictures of a sort to describe the type. Generally, in a declaration like int declarator, declarator gives a picture of some expression we will use as an int. For example, int *foo says *foo will be an int, so it declares foo to be a pointer to an int. Another example is that int foo[3] says foo[i] will be an int, so it declares foo to be an array of int. Note that these are declaring foo, not *foo or foo[i], so, when an initial value is given, it is for initializing foo, not *foo or foo[i].
21 is not a proper value for a pointer, so the compiler complains. (Integers can be converted to pointers by using casts, but this is a special use case that requires ensuring the integers represent addresses made available in the C implementation.)