Personally, I stick to using the associative version of fetching functions, such as [man]mysql_fetch_assoc/man. By default, [man]mysql_fetch_array/man will duplicate the results - one set will have associate array indexes (e.g. "column_name") whereas the other set will be a numerically indexed array (where index 0 corresponds to the first column in the result set, index 1 is the second column, etc.). Since $row[3] doesn't really tell you much and is harder to understand than $row['first_name'], I see very little use for the numerically indexed version.
As far as how the mysql_fetch_*() functions work in general... I'd first suggest reading over the description for the function and looking at the coding examples. I'm not sure how else to say it other than how the manual has already said it.
Take [man]mysql_fetch_assoc/man, for example. Assuming you've sent a valid query (e.g. mysql_query() didn't return FALSE), then the first call to mysql_fetch_assoc() will retrieve the first row of the result set as a 1-dimensional array and return that array. It also advances an internal data pointer to point to the next row. If there is no 'next row,' mysql_fetch_assoc() returns FALSE, letting you know that you've reached the end of the result set. Otherwise, it returns another array with the values from the second row while advancing the internal pointer again to the next row.
Can you tell us more about what it is specifically that you don't understand?