I have no idea how much you know about computer memory, so this might go way over your head... but I'll try anyway.
Memory in your computer has addresses. Let's say 0x1000 is an address in memory, and this is where your array begins. Each address in memory has some value in it.
I'm going to use MEMORYLOCATION->CONTENTS OF MEMORY throughout this post.
0x1000->arr[0]
Let's say your array is of type integer, and an integer takes 32 bits or 4 bytes on your system.
That means the next element of your array will be 4 bytes later in memory.
0x1004->arr[1]
So addresses 0x1000-0x1003 are for array element 0. 0x1004-0x1007 are for array element 1, etc.
When you declare your array and its size, a block of memory is set aside so only that array can use it. So when you declare your array (int arr[10]), it tells the computer to reserve 10 * 4 bytes = 40 bytes of memory. If it starts at 0x1000, then 0x1000 - 0x1039 to be reserved for that array.
When you say just arr, you are actually calling out the memory address for arr[0], because the variable name for an array is always a pointer to the first element in the array (if you don't specify the index). Putting an asterisk before a pointer means to tell you the contents of memory at that address.
So *(arr) will return the contents of memory at 0x1000, which happens to be the same as arr[0].
When you do arr + i, the compiler is smart enough to know how many bytes to add to arr because it knows the type of the array. Since it is int, it will add 4 * i.
So *(arr + 2) means to start at 0x1000 (because that is the beginning of the array), then to add two elements (each element is 4 bytes, so 2 * 4 bytes = 8). The result is 0x1008. Now since you have the *, you will look at the memory contents at 0x1008, which happens to be arr[2].
So to answer your question, yes, it is looking at a specific address that is allocated to that array and then getting the contents at that address (assuming you add an index that is within your array, otherwise that is a very dangerous thing to do).
This will either make a lot of sense or confuse you a whole lot. If it confuses you, let me know which part is confusing.
Wow, that block of text made things a lot more clear than I expect it to be. Thank you so much.
To follow up, if *(arr+i) is looking at the address of the array, with the compiler knowing whether it's 4 or 8 bytes, how would it differ if let's say it was *((arr)+i)? or is it pointing to the same address?
Thank you so much for the replies. I guess I was mixing up between this and pointers, as I recall my teacher saying something about pointers being read differently when brackets are put, something along the line of *(p+1) would be different from (*p+1). I'll go back to my text book and see if I can dig out the difference between the two that he was talking about.