인프런 커뮤니티 질문&답변

용개형멋져님의 프로필 이미지
용개형멋져

작성한 질문수

홍정모의 따라하며 배우는 C언어

10.13 포인터의 배열과 2차원 배열

9분 20초경 질문

작성

·

412

·

수정됨

2

*(parr[j] + i)과 *(*(parr + j) + i))는 어느정도 이해를 했습니다.

예를 들면

*(parr[0] + 1)

parr[0]에 담긴 데이터는 주소이고 그 주소는 arr0이라는 포인터와 비슷한 무언가를 가리킨다.
그리고 arr0이라는 포인터와 비슷한 무언가는 arr0[0]의 주소를 가리킨다.
그리고 arr0[0]은 arr0의 첫 번째 공간을 의미한다.
그러므로 parr[0]은 arr0[0]을 가리키는 것이고 parr[0]에 1을 더하면 arr0[0]의 다음 공간인 arr0[1]을 가리키게 되고
*로 그 공간에 접근하게 된다.

*(*(parr + 1) + 1)
parr은 arr0을 가리킨다. 그리고 arr0은 arr0[0]을 가리킨다.
그러므로 parr에 1을 더하면 arr0[1]을 가리키게 된다.
거기에 *를 붙이면 arr0[1]의 공간에 있는 데이터에 접근을 한다.
거기서 1을 더하면 그 데이터에 1을 더하게 되는 것이다.

이렇게 이해를 했는데 맞는거겠죠?

이거 말고 질문이 있는데

parr[j][i]와 (*(parr + 1))[1]의 맨앞에 *가 없는데도 어떻게 그 공간에 접근해서
printf 함수로 출력을 했을 때 배열의 주소가 아닌 배열의 데이터가 출력 될 수 있는 건지 궁금합니다.
parr[j]는 배열의 주소가 나오는데 parr[j][i]는 왜 배열의 값이 나오는지 이해가 안되요.

감사합니다

답변 3

3

안녕하세요, 답변 도우미 Soobak 입니다.

 

질문 1)
| (parr[j] + i)과 (*(parr + j) + i))는 어느정도 이해를 했습니다. 예를 들면 (parr[0] + 1) parr[0]에 담긴 데이터는 주소이고 그 주소는 arr0이라는 포인터와 비슷한 무언가를 가리킨다. 그리고 arr0이라는 포인터와 비슷한 무언가는 arr0[0]의 주소를 가리킨다. 그리고 arr0[0]은 arr0의 첫 번째 공간을 의미한다. 그러므로 parr[0]은 arr0[0]을 가리키는 것이고 parr[0]에 1을 더하면 arr0[0]의 다음 공간인 arr0[1]을 가리키게 되고 로 그 공간에 접근하게 된다. ((parr + 1) + 1) parr은 arr0을 가리킨다. 그리고 arr0은 arr0[0]을 가리킨다. 그러므로 parr에 1을 더하면 arr0[1]을 가리키게 된다. 거기에 *를 붙이면 arr0[1]의 공간에 있는 데이터에 접근을 한다. 거기서 1을 더하면 그 데이터에 1을 더하게 되는 것이다.
이렇게 이해를 했는데 맞는거겠죠?
: 네, 거의 올바르게 이해하셨지만, 약간의 혼동이 있으신 것 같아 명확히 해드리도록 하겠습니다.

  • *(parr[0] + 1)

    • parr[0]arr0 을 가리키는 포인터입니다.

    • parr[0] + 1arr0 배열의 두 번째 원소(arr0[1])를 가리킵니다.

    • *(parr[0] + 1)arr0[1] 의 값을 의미합니다.

  • *(*(parr + 1) + 1)

    • parr 자체는 포인터의 배열을 가리키는 포인터, 즉, 이중 포인터입니다.

    • parr + 1 은 두 번째 배열 포인터, 즉, arr1 을 가리킵니다.

    • *(parr + 1)arr1 배열 자체를 가리킵니다.

    • *(parr + 1) + 1arr1 배열의 두 번째 원소(arr1[1])를 가리킵니다.

    • *(*(parr + 1) + 1)arr1[1] 의 값을 의미합니다.

즉, parr 은 포인터의 배열을 가리키는 이중 포인터이며, parr + jj 번째 배열을 가리키는 포인터를 의미합니다.
따라서, *(*(parr + j) + i)j 번째 배열의 i 번째 원소의 값을 의미합니다.

 

질문 2)
| parr[j][i]와 (*(parr + 1))[1]의 맨앞에 *가 없는데도 어떻게 그 공간에 접근해서
printf 함수로 출력을 했을 때 배열의 주소가 아닌 배열의 데이터가 출력 될 수 있는 건지 궁금합니다.
parr[j]는 배열의 주소가 나오는데 parr[j][i]는 왜 배열의 값이 나오는지 이해가 안되요.

: 가장 뒤의 [] 인덱스 접근 연산자의 의미에 대해서 생각해보시면 좋을 것 같습니다.

보충 설명을 드리면 다음과 같습니다.

  • parr[j][i] 의 동작 방식
    parr 는 이중 포인터이며, 포인터의 배열을 가리킵니다. 여기서, 각 포인터는 다른 배열을 가리킵니다. (arr0, arr1 등)
    이 때, parr[j]j 번째 배열을 가리키는 포인터입니다. 따라서 parr[j] 자체는 주소를 나타냅니다.
    parr[j][i]j 번째 배열의 i 번째 요소에 접근합니다.
    parr[j] 가 배열의 주소를 가리키므로, [i] 를 추가하면 그 배열의 i 번째 요소에 접근하는 것이 됩니다.
    결과적으로, parr[j][i] 는 주소가 아닌 해당 배열의 i 번째 요소의 값을 나타냅니다.

  • (*(parr + j))[i] 의 동작 방식


    parr + jparr 이 가리키는 배열의 j 번째 포인터를 가리킵니다.
    즉, 이는 parr[j] 와 동일합니다.
    *(parr + j)j 번째 배열 자체를 의미합니다. 여기서, 이 배열은 parr[j] 가 가리키는 배열과 동일합니다.
    (*(parr + j))[i]*(parr j) 가 가리키는 배열의 i 번째 요소에 접근하는 것입니다.
    이는 parr[j][i] 와 동일한 방식으로 동작합니다.
    결과적으로, 이 표현은 배열의 i 번째 요소의 값을 나타내게 됩니다.

     

     

    따라서, parr[j][i](*(parr + j))[i] 모두 배열의 주소가 아닌, 배열의 특정 요소의 값에 접근합니다.
    parr[j] 는 이중 포인터이기 때문에 주소를 반환하지만, parr[j][i] 는 그 주소가 가리키는 배열의 i 번째 요소의 값을 반환합니다.
    따라서, printf() 와 같은 함수를 사용할 때 배열의 데이터 값을 출력할 수 있는 것입니다.

     

    혹시, 잘 이해가 안되시거나 어려우신 부분 있으시면 편하게 댓글 남겨주세요.


선생님 parr[0]은 arr0을 가리킨다고 하면 이해가 잘 안되고
parr이 arr0을 가리키고 parr[0]은 arr0이라고 하면 이해가 잘 돼요.

제가 틀린걸까요?
항상 답변 정말정말 감사합니다.

네, 그렇게 이해하시는 것도 정확합니다.

배열의 이름은 그 배열의 첫 번째 원소의 주소를 가리키는 포인터와 호환이 되는 형태라는 점을 생각해보시면 좋을 것 같습니다.

int arr0[3] = {1, 2, 3}; , int arr1[3] = {4, 5, 6};
그리고 int* parr[2] = {arr0, arr1} 에서,

parr 은 배열의 이름이므로, 배열의 첫 번째 원소 arr0 의 주소를 가리킵니다.
마찬가지로, parr[0]arr0 을 가리키는 포인터입니다.
즉, parr[0]arr0 배열의 첫 번째 원소인 arr[0] 의 주소를 가리키고 있습니다.

좋은 질문해주셔서 저 또한 감사드립니다.
혹시 이해가 안되시거나 어려우신 부분있으시면 편하게 댓글 남겨주세요.

또 질문해서 정말 죄송합니다. 복습 하다 보니 이해가 안 가는 부분이 또 생겨 질문 드립니다.

parr도 arr0을 가리키고 parr[0]도 arr0을 가리키는데

parr에 1을 더하면 arr1을 가리키고 parr[0]에 1을 더하면 arr0[1]을 가리키는 게 혼동이 돼서 이해가 잘 안됩니다.

한번 더 알려주시면 정말 감사하겠습니다.

여러가지 개념이 혼동되어서 너무 어렵네요

배열의 이름은 배열의 첫 번째 공간의 주소라는 걸 기억해내면

parr은 arr0을 가리키는 거고, parr[0]은 arr0[0]을 가리키는 거다.

라고 해석이 가능할 것 같은데 선생님은 parr[0]이 arr0을 가리키는 포인터라고 하셨으니 틀린 해석 인걸까요?

질문해주셔서 감사합니다. 질문은 편하게 해주세요!
학습에 있어서 이해가 어려운 부분을 명확히 하는 것이 중요한 것 같습니다.

parrparr[0] 이 가리키는 대상에 대해서 설명드립니다.

 

  • parrint* 타입의 포인터들을 원소로 가지는 배열입니다.
    즉, parr 자체는 int* 타입의 포인터를 가리키는 포인터 배열의 시작 주소입니다.
    (이 부분이 이중 포인터 개념 때문에 조금 복잡할 수 있으나, 차근 차근 이해해보시면 생각보다 어렵지 않은 내용입니다!)

    • parr1 을 더했을 때의 동작
      : parr1 을 더하면, parr 은 다음 int* 타입의 포인터 원소를 가리키게 됩니다.
      이 때, 배열의 포인터 연산은 해당 배열의 원소 타입의 크기에 따라서 주소가 이동한다는 점이 중요합니다.
      parr 배열의 원소 타입은 int* 이므로, parr + 1arr1 을 가리키는 포인터의 위치로 이동합니다.

 

  • 반면, parr[0]int* 타입이며, arr0 배열의 첫 번째 원소, 즉, arr0[0] 의 주소를 가리킵니다.

    • parr[0]1 을 더했을 때의 동작
      : parr[0]1 을 더하면, parr[0]arr0 배열의 다음 원소인 arr0[1] 을 가리키게 됩니다.
      여기서 중요한 점은, 위에서 강조드렸던 부분, 즉, parr[0] 의 타입이 int* 이기 때문에, 포인터 연산은 int 타입의 크기만큼 주소가 이동한다는 것입니다.
      즉, parr[0] + 1arr0[1] 의 주소를 가리키게 됩니다.

 

  • "parr 은 arr0 을 가리키는 거고, parr[0] 은 arr0[0] 을 가리키는 거다." 라고 해석하신 부분 역시 옳습니다.

    parrint* 타입의 포인터들을 원소로 가지는 배열의 이름입니다.
    이 배열에서 parr 자체는 포인터 배열의 첫 번째 원소인 arr0 의 주소를 가리키는 포인터입니다.
    즉, parrarr0 을 가리키고 있으며, 이는 arr0 이라는 배열의 시작 주소와 동일합니다.

    반면, parr[0] 은 포인터 배열의 첫 번째 원소로, arr0 의 주소를 저장하고 있는 int* 타입의 포인터입니다.
    parr[0] 이 가리키는 대상은 arr0 배열의 첫 번째 원소, 즉, arr0[0] 입니다.

    따라서, parrarr0 배열을 가리키는 포인터입니다.(보다 명확히는, 배열의 이름은 포인터와 호환이 되는 형태라는 표현이 맞지만, 이해의 편의를 위해 여기서는 포인터라고 하겠습니다.)
    parr[0] 역시 arr0 배열을 가리키며, 동시에 배열의 첫 번째 원소인 arr[0] 의 주소를 가리킵니다.

     


이렇게, parrparr[0] 은 같은 메모리 주소를 가리키더라도, 타입이 다르기 때문에 포인터 연산의 결과도 달라지게 되는 것입니다.
혼동하기 쉬운 부분이므로, 자신감을 가지시고 충분히 이해하고 넘어가시는 것이 좋습니다.
추가 질문이 있으시면 언제든지 물어보세요.

1

*(*(parr + 1) + 1)
제가 이걸 좀 틀리게 해석한 것 같네요 제가 다시 해석을 해보자면

parr은 arr0을 가리킨다. 그러므로 parr에 1을 더하면 arr1을 가리키게 된다.
거기에 *를 붙이면 arr1이 가리키는 arr1[0]을 가리키게 되고, 또 거기에 1을 더하면 arr1[0][1]을 가리키게 되고,
또또 거기에 *를 붙이면 arr1[0][1]의 공간에 있는 데이터에 접근을 하게 된다.

이렇게 해석이 되는 거 같은데 맞는건가요?

네, 맞습니다.
잘 이해하고 계십니다!! 👍👍

1

parr[j][i]에서

parr[0]은 arr0이고 parr[1]은 arr1이니 주소가 나오는 것이고

parr[0][0]은 arr0[0]이고 parr[1][0]은 arr1[0]이라 데이터가 나오는 것이 맞나요?

제가 이해한 게 맞았으면 좋겠네요

네, 정확하게 이해하고 계십니다. 👍😀

용개형멋져님의 프로필 이미지
용개형멋져

작성한 질문수

질문하기