a랑 &a[0]은 같은데 이게 얼마나 같냐면

진짜로 같다.

무슨 면에서냐면 &와 *의 효과는 컴파일될 때 서로 상쇄된다는 점에서.

int *ptr; // 쓰레기 값으로 초기화된 포인터
printf("%d\n", *ptr);

이건 보통 오류가 뜨겠지만

int *ptr;
printf("%p\n", &*ptr);

이건 *ptr에 대한 메모리 접근이 실제로 이루어지지 않아서 상관없다.

int *arr = malloc(72);
free(arr); // malloc이 실패했어도 free는 안전함 (상식으로 알아둬라)
printf("%p\n", &arr[0]);

같은 이유로 이것도 상관없지.

이런 식으로 실제의 값 평가가 없이 실행되는 게 몇 가지 있는데 sizeof도 그러하다.

int *ptr;
printf("%d\n", sizeof *ptr); // 아무 문제 없음. sizeof(int)가 출력된다

그나저나 또다른 상식으로 sizeof 뒤에 자료형이 붙을 때가 아니면 괄호는 생략 가능하다는 것도 알아둬라.

int n, *p;
printf("%d %d %d\n", sizeof + n, sizeof - n, sizeof * p); // 이렇게 띄어쓰기를 넣으면 멍청한 새끼들을 헷갈리게 만들 수 있다