So a pointer contains a number where 0 is the first byte(8 bits per byte) in the process memory address space, 1 is the second and so on. When you want to access the value that the pointer points to (the contents stored in the address with that numerical index), you then deference the pointer. You use a language specification notation to tell the compiler or interpreter what you are now interested in.
Consider in C, given a pointer such as p below...
const char* p = "abc";
Four bytes with numerical values used to encode the characters 'a', 'b', 'c' and a null byte to denote the end of the textual data. These characters are stored somewhere in memory and the numerical address of that data is stored in the variable p. If you want to refer to the characters that p points to, you could use:
assert(*p == 'a'); // the first character at address p will be 'a'
assert(p == 'b'); // p actually dereferences a pointer created by adding
// p and 1 times the size of the things to which p points:
// in this case they're char, typically 1 byte...
assert(*(p + 1) == 'b'); // another notation for p
You can do things like this if you have some data that can be written to...
int x = 2;
int* p_x = &x; // put the address of the x variable into the pointer p_x
*p_x = 4; // change the memory at the address in p_x to be 4
assert(x == 4); // check x is now 4
You must have known at compile time that you would need a variable called x, and the code asks the compiler to arrange where it should be stored. Ensuring the address will be available by &x. Sometimes you don't know how much memory you'll need until your program is running and sees what data is thrown at it. You then can dynamically allocate memory using new:
int* p = new int; // get some memory somewhere...
*p = 10; // dereference the pointer to the memory, then write a value in
fn(*p); // call a function, passing it the value at address p
(*p) += 3; // change the value, adding 3 to it
delete p; // release the memory back to the heap allocation library