Yes, a pointer is a data type. The purest form of which (mainly talking about C here) is void *
. A void *
can be used to pass a memory address around (which is what a pointer is), but it can't be dereferenced. Dereferencing a pointer is what you do to get at the data contained at the memory location the pointer is pointing at, which implies that you know what type of data you're reading from the memory. The type determines how much memory will be read, and because a void
is "nothing". A void *
can be set to point at any block of memory, that can contain any type, so you can cast a void *
to any other pointer type (int *
, for example), and dereference that, instead.
Each type we have is used to store a specific piece of data (value), we use a char
to store a single character, an int
to store an integer, double
to store double precision decimals and so on. None of these types are used to store locations in memory, apart from pointers. So just like the other types, a pointer is used to store a specific piece of data. And the mother of all pointers is void *
.
Sadly, this void *
is rather restricted: you can't dereference it, you can't use it for pointer arithmetic (not according to the standard anyway). So C provides you with a series of derived pointer types, that make life easier: char *
, int *
, double *
and so on.
What they are, really, is short-hand for: (char *) void * my_ptr;
Some more attempts at making my point as clearly as possible:
Pointers have their own size, irrespective of the type they're said to point at:
char a_character = 'a'; //type: a char
char *a_char_ptr = &a_character; //memory address, in this case, the one holding a_charachter
The distinction is probably best seen by looking at the sizes of both these vars:
printf("%zu <> %zu\n", sizeof a_character, sizeof a_char_ptr);
The code above will give you something like "1 <> 8" or "1 <> 4", depending on what system you're on. Numbers represent the size, in bytes.
Pointers also have their own printf
format specifier: %p
:
printf("%c is the value stored at %p\n", *a_char_ptr, (void *) a_char_ptr);
To print the actual memory address (the actual value of a pointer), you are required to cast the pointer to the generic void *
type. A void pointer is sort of the generic pointer; it's the pointer that makes no assumptions as to the data it is pointing at. This is what malloc
, calloc
and realloc
return, a generic pointer, that can be set to point at any other type. So what is a char *
? It's a generic pointer type, set to point at blocks of memory of 1 byte in size (sizeof(char)
). In a sense, a typed pointer, then, is a derived type, but think of it like this: char *
is short for (char *) void *my_ptr;
But really, what is a type? The gist of it is that a type is way to determine how data in memory is supposed to be interpreted. A variable of the type char
represents a character. A variable of the type int
represents an integer. Same applies to pointers: char *x
is not of the type char
, it's of the type char *
(pointer to char). This means that char *x
itself is a location in memory we can use to read one or more char
values.
I could rant on for a while but TL;TR:
Yes, a pointer is a data type (void *
in its purest form). The pure form is quite unusable (because you can't dereference it). Instead of having to cast the pointer every time you decide to use it, C offers the convenience of derived pointer types (like char *
, int *
and so on). But really, they're pointers, and therefore a data-type in their own right.