Array indices are just a convenient and more meaningful way of doing addition.
Hence the first element in any array, static or dynamic, is 0 as you add 0 to find it.
char *szDynamicStr = new char[12];
strcpy(szDynamicStr, "hello world");
printf("%c == %c\n", szDynamicStr[0],
*(szDynamicStr + 0));
Both
szDynamicStr[0]
and
*(szDynamicStr + 0)
compile to exactly the same code, because the array translates to the pointer offset.
The pointer is pointing to the first element in the array, so offset 0 is the first element, offset 1 is the 2nd element, etc...
If this answers your question and you don't care why it works stop reading here or you might get confused.
When adding a number to a pointer, the number is multiplied by the size of the data that is getting pointed to (in this case 1 byte for a single ASCII char) by the compiler.
This is better explained in code:
DWORD pNumbers[] = { 0xD00D, 0xBADF00D };
printf("%u == %u\n", pNumbers[1],
*(pNumbers + 1));