Look at the underlying assembly code:
6: int main(int argc, char* argv[])
7: {
00EA1250 push ebp
00EA1251 mov ebp,esp
00EA1253 sub esp,58h
00EA1256 mov eax,dword ptr [___security_cookie (0EA6000h)]
00EA125B xor eax,ebp
00EA125D mov dword ptr [ebp-4],eax
00EA1260 push ebx
00EA1261 push esi
00EA1262 push edi
8: char c = 'A';
00EA1263 mov byte ptr [ebp-5],41h
9: char *pc = &c;
00EA1267 lea eax,[ebp-5]
00EA126A mov dword ptr [ebp-0Ch],eax
10: char arr[] = "ABCDEFG";
00EA126D mov eax,dword ptr [string "ABCDEFG" (0EA4740h)]
00EA1272 mov dword ptr [ebp-14h],eax
00EA1275 mov ecx,dword ptr ds:[0EA4744h]
00EA127B mov dword ptr [ebp-10h],ecx
11: char *pa = arr;
00EA127E lea eax,[ebp-14h]
00EA1281 mov dword ptr [ebp-18h],eax
12:
13: printf("%c\n", *pc); /* as you say */
00EA1284 mov eax,dword ptr [ebp-0Ch]
00EA1287 movsx ecx,byte ptr [eax]
00EA128A push ecx
00EA128B push offset string "%c\n" (0EA473Ch)
00EA1290 call dword ptr [__imp__printf (0EA7238h)]
00EA1296 add esp,8
14: printf("%c\n", *pa); /* as you say */
00EA1299 mov eax,dword ptr [ebp-18h]
00EA129C movsx ecx,byte ptr [eax]
00EA129F push ecx
00EA12A0 push offset string "%c\n" (0EA473Ch)
00EA12A5 call dword ptr [__imp__printf (0EA7238h)]
00EA12AB add esp,8
15: printf("%c\n", pc[0]); /* not as you say, but perfectly legal */
00EA12AE mov eax,dword ptr [ebp-0Ch]
00EA12B1 movsx ecx,byte ptr [eax]
00EA12B4 push ecx
00EA12B5 push offset string "%c\n" (0EA473Ch)
00EA12BA call dword ptr [__imp__printf (0EA7238h)]
00EA12C0 add esp,8
16: printf("%c\n", pa[0]); /* not as you say, but perfectly legal */
00EA12C3 mov eax,dword ptr [ebp-18h]
00EA12C6 movsx ecx,byte ptr [eax]
00EA12C9 push ecx
00EA12CA push offset string "%c\n" (0EA473Ch)
00EA12CF call dword ptr [__imp__printf (0EA7238h)]
00EA12D5 add esp,8
17: }
00EA12D8 xor eax,eax
00EA12DA pop edi
00EA12DB pop esi
00EA12DC pop ebx
00EA12DD mov ecx,dword ptr [ebp-4]
00EA12E0 xor ecx,ebp
00EA12E2 call @ILT+10(@__security_check_cookie@4) (0EA100Fh)
00EA12E7 mov esp,ebp
00EA12E9 pop ebp
00EA12EA ret
And specifically at these two lines for example:
13:printf("%c\n", *pc); /* as you say */
00EA1284 mov eax,dword ptr [ebp-0Ch]
00EA1287 movsx ecx,byte ptr [eax]
00EA128A push ecx
00EA128B push offset string "%c\n" (0EA473Ch)
00EA1290 call dword ptr [__imp__printf (0EA7238h)]
00EA1296 add esp,8
15:printf("%c\n", pc[0]); /* not as you say, but perfectly legal */
00EA12AE mov eax,dword ptr [ebp-0Ch]
00EA12B1 movsx ecx,byte ptr [eax]
00EA12B4 push ecx
00EA12B5 push offset string "%c\n" (0EA473Ch)
00EA12BA call dword ptr [__imp__printf (0EA7238h)]
00EA12C0 add esp,8
Now consecutively, line-by-line:
13:printf("%c\n", *pc); /* as you say */
15: printf("%c\n", pc[0]); /* not as you say, but perfectly legal */
284 mov eax,dword ptr [ebp-0Ch] // 13
2AE mov eax,dword ptr [ebp-0Ch] // 15
287 movsx ecx,byte ptr [eax] // 13
2B1 movsx ecx,byte ptr [eax] // 15
28A push ecx // 13
2B4 push ecx // 15
28B push offset string "%c\n" (0EA473Ch) // 13
2B5 push offset string "%c\n" (0EA473Ch) // 15
290 call dword ptr [__imp__printf (0EA7238h)] // 13
2BA call dword ptr [__imp__printf (0EA7238h)] // 15
296 add esp,8 // 13
2C0 add esp,8 // 15
They resolve to the exact same fundamental operations on the machine,
despite their being different logically in C.
> In C, you can't pass arrays, because the use of the array name in a
> value context (and parameter-passing *is* a value context, because it is
> the value that is passed) is treated as a pointer to the first element
> of the array.
That's a C protocol. You can perform the exact same operation by using
the address of the array through a union and reconstituting it on the
other side of the call.
Datawise it's no different, only protocol-wise.