why do we need another type of data to store the address
This is really the right question, and the answer is hidden in it - why do we need a different type of data to store addresses. We (programmers) need this. The machine does not care - one number is exactly the same as another.
If you think of a “variable” as a placeholder for some data, there is a dichotomy in programming languages between using the data itself and using the variable. Sometimes you only need data (for example, you need to print it), and sometimes you need an address where this data is stored (for example, so that you can change it). Most languages have syntactic sugar, which confuses the two cases, and treats the variable identifier differently in different contexts.
One such case is as follows: consider the statement
a = 1;
In this case, the compiler looks at the address of the variable identified by "a" and writes "1" to that address. Identifier "a" may also be a pointer. Now let's look at another thing:
if (a == 1) ... ;
You do not compare the address of the variable identified by "a" with something, you compare what is stored at this address with "1".
Various "types of pointers" exist again for the convenience of programmers: basically, we make fewer errors that erroneously access data. Take a look at this example:
double d; double* dp; int i; int* ip;
By default, C is very relaxed, and you can usually do:
dp = ip;
or
dp = &i;
... but! sizeof (i) == 4 and sizeof (d) == 8, so if you search for a dp pointer, you will try to read 8 bytes from variable (i), which contains only 4. This means that you will read 4 unpredictable (random) bytes after the first four bytes of i, which is definitely what you don't want to do.
Again, all this is for our convenience. The car does not care. Both pointers look and behave exactly the same as in the CPU.