Suppose you have the following code in a program:
int val;
int *ptr;
Now, take a clean sheet of paper and on the right side near the top write val. Draw a 45 degree line from the base of val down and to the right. Label the line rvalue. The term comes from assembly language days and stands for "register value". Now draw another 45 degree line from the base of val down and to the left and label it lvalue. (From assembly again for "location value".) Let's pretend the compiler placed val at memory location 1000, so write that number at the end of your lvalue line. My Bucket Analogy likens what you've drawn to a bucket: the lvalue tells you where the bucket is located in memory, the rvalue tells you what is inside the bucket, and the int type specifier tells you that the bucket is big enough to hold 2 bytes of data. (An int on other platforms might be a 4 byte bucket.)
Now on the left side of the sheet, draw the same lvalue/rvalue diagram for ptr. We'll pretend that the compiler placed this variable at memory location 1050, so write that number at the base of its lvalue. Now consider this code:
val = 10;
To perform this assignment, the code need to know what value to assign and where to put it in memory. Assignments change the rvalue of a variable, so the code "goes" to memory address 1000, takes 2 bytes of data that has a binary 10 in them, and pours them into val's bucket. The rvalue of val is now 10.
Now consider the statement:
ptr = &val;
This is also an assignment, but fashioned for a pointer assignment. The address of operator (the ampersand, &, in front of val) says that, instead of doing an rvalue-to-rvalue assignment, get the address of val (i.e., 1000) and assign it to ptr. This means that ptr now has an rvalue that is the memory address of where the val bucket resides in memory. This leads to an important fact about pointers: A valid pointer should only store one of two things: 1) a null value, which means the pointer points to nothing useful, or 2) a valid memory address where some data are stored.
Now consider the statement:
*ptr = 20;
The indirection operator (*) says: "Go to the memory address held in ptr's rvalue (i.e., 1000), and place 2 bytes of data there which holds the value of 20." The process, called indirection, allows you to change the rvalue of one variable (val) using a pointer (ptr). Note that the type specifier for ptr (an int) is critical, because it tells the pointer how big the bucket is. If by mistake you define ptr as:
long ptr;
the assignment using indirection would likely fail because you are trying to pour 4 bytes of data into a 2 byte bucket. Also, something like:
ptr++;
always changes the rvalue of the pointer by the size of its type specifier, or a 2 byte displacement for an int but a 4 byte change for a long or a float.
I hope this helps.