작성
·
770
3
안녕하세요 포인터 2차원 배열 9분 대부터 다시 공부하다가 이해가 가지 않아 문의드립니다.위의 코드에서 *(*(parr + j) + i));와 (parr[j] + i);가 헷갈립니다. 일단 제가 처음에 이해한 대로 설명드려보겠습니다. parr은 parr[0]을 가리키는 포인터이며 즉 이 포인터가 최종적으로 가리키는 공간은 &arr0[0] 즉 arr 배열의 첫번째 공간입니다. 이 상태에서 예를 들어 parr + 1을 하면 &arr0[1]로 이동하며 를 하면 그 값은 2 이런 식이라고 생각했습니다. 또한 parr[1] + 1에서 parr[1]은 포인터이며 이 포인터가 가리키는 공간은 &arr1[0]이며 이 상태에서 포인터 연산 +1을 해주면 &arr1[1]이고 이를 로 접근하면 그 값은 5로 이해했습니다. 그치만 실제 결과를 보면 위에서 제가 제시한 두 코드의 결과는 같아야 되더군요. 그렇다면 그 결과를 도출하기 위해 다시 *(*(parr + j) + i));를 설명드리겠습니다. parr 배열 이름 자체는 &parr[0]이며 이 상태에서 포인터 연산 + 1을 하면 &parr[0] = &arr0[0], &parr[0] + 1 = &arr0[1]이 아니라 &parr[0] + 1 = &parr[1]이며 이 상태에서 로 접근하면 그 값은 4, 4+1은 5이며 이를 *로 간접 접근하면..이런 식인데 어디부터 제 개념이해가 잘못됐는지 모르겠습니다.. parr[1]로 먼저 arr1[0]에 접근한 뒤 포인터 연산 +1을 하는 것과 parr 자체에서 포인터 연산 +1을 하면 결과가 정확히 어떻게 다르며 *(*(parr + j) + i)) 이 코드의 정확한 개념이 궁금합니다.
답변 1
4
안녕하세요, 답변 도우미 Soobak 입니다.
해당 예시 코드에서,
arr0
과 arr1
은 각각 3개의 정수를 저장하는 1차원 배열이며, parr
은 이 두 배열의 주소를 저장하는 포인터 배열 입니다. 그러므로, parr[0]
은 arr0
의 첫 번째 원소의 주소를 가리키고, parr[1]
은 arr1
의 첫 번째 원소의 주소를 가리킵니다.
포인터 연산에서 +1
연산을 하면, 해당 포인터가 가리키는 데이터 타입의 크기만큼 이동합니다.int
포인터의 경우, int
자료형의 크기만큼 이동합니다.
위 내용을 바탕으로 코드를 살펴보면서 설명드리겠습니다.
1. parr[j][i]
parr[j][i]
에서, parr[j]
는 arr0
또는 arr1
의 첫 번째 원소를 가리키는 포인터입니다.
따라서, parr[j][i]
는 j
번째 배열의 i
번째 원소를 가리키게 됩니다.
2. *(parr[j] + i)
*(parr[j] + i)
의 경우, parr[j]
는 j
번째 배열의 첫 번째 원소를 가리키는 포인터이고, 여기에 i
를 더하면 j
번째 배열의 i
번째 원소를 가리키는 포인터가 됩니다.
따라서, *(parr[j] + i)
는 j
번째 배열의 i
번째 원소를 가리키게 됩니다.
3. *(*(parr + j) + i)
여기서, (parr + j)
는 j
번째 배열의 첫 번째 원소를 가리키는 포인터를 가리키는 포인터 입니다.
따라서, *(parr + j)
는 j
번째 배열의 첫 번째 원소를 가리키는 포인터가 되고, 여기에 i
를 더하면 j
번째 배열의 i
번째 원소를 가리키는 포인터가 됩니다.
즉, *(*(parr + j) + i)
는 j
번째 배열의 i
번째 원소를 가리키는 것입니다.
따라서, parr[j][i]
, *(parr[j] + i)
그리고 *(*(parr + j) + i)
모두 같은 값을 가리키게 됩니다.
안녕하세요, 답변 도우미 Soobak 입니다.
배열의 이름이 가리키는 것, 그리고 []
으로의 접근, 포인터 연산 3가지 개념에 대해서 혼동하고 계신 것 같습니다.
위 개념들에 대해서 정리해보시면 좋을 것 같습니다.
우선, 질문주신 내용에서 잘 이해하고 계신 것 같은 특정 부분을 다른 문장에서는 잘못 표현하고 계셔서, 제가 질문자님께서 정확히 어느 것을 오해하고 계신 것인지 알기가 어렵네요. 🥲
그래서 이번에는 질문주신 문장 모두에 대해서 설명드리는 식으로 답변을 드려보도록 하겠습니다.
말씀해주신 부분에서 parr이라는 포인터 자체가 가리키는 것은 parr[0]-->arr0[0]인가요?? parr[0]이 arr0[0]을 가리키고 parr[1]은 arr1[0]을 가리키는 것은 이해했으나 parr이라는 포인터가 자체적으로 가리키는 것은 무엇인지 궁금합니다. 예를 들어 arr0이라는 배열 이름 자체는 arr0[0]을 가리키듯이요.
: int* parr[2] = {arr0, arr1};
로 선언된 parr
은 포인터의 배열 입니다.
배열의 이름은 배열의 첫 번째 원소의 주소를 가리키며, C언어에서 문법적으로 포인터와 호환이되는 형태입니다.
따라서, parr
은 arr0
의 주소를 가리킵니다.
이 때, arr0
은 int arr0 = {1, 2, 3};
으로 선언된 int
의 배열 입니다.
따라서, arr0
이 가리키는 것은 배열의 첫 번째 원소인 1
이 저장된 메모리 공간의 주소를 가리킵니다.
위에서 설명드린 것 처럼 parr
은 arr0
의 주소를 가리키는 포인터, arr0
은 1
의 주소를 가리키는 포인터이므로, parr
은 1
의 주소를 가리키는 포인터를 가리키는 포인터, 즉 이중 포인터로 볼 수 있습니다.
또한 위에서도 말씀드렸었지만 parr[j]+i가 parr의 j번째 배열의 i번째 원소를 가리키는 과정과 *(*(parr + j)+i)가 j번째 배열의 i번째 원소를 가리키는 과정이 어떻게 다른지 구체적으로 알고 싶습니다. parr[j]가 parr의 j번째 배열의 첫 번째 원소를 가리킨다면 parr + j는 parr[0] 즉 parr의 첫번째 배열을 가리키는 포인터에 j를 더해 parr의 j번째 배열에 접근하는 것인가요..?
: C언어에서 배열 인덱스 연산자인 [정수]
으로 배열의 요소에 접근하는 것과, *(포인터 + 정수)
로 요소에 접근하는 것은 기본적으로 동일한 과정입니다. 다르지 않습니다.
즉, parr[j]
와 *(parr + j)
는 동일하며, *(parr[j] + i)
와 *(*(parr + j) + i))
은 동일한 표현입니다.
첫 번째 문장, "...parr[j]+i가 parr의 j번째 배열의 i번째 원소를 가리키는 과정과..." 에서, parr[j] + i
는 j
번째 배열의 i
번째 원소를 가리키는 것이라고 표현하기 보다는 해당 원소를 가리키는 포인터, 즉, 해당 원소의 주소값을 가리키는 것으로 이해하시는 것이 보다 정확합니다.
마지막 문장에서 이해하신 내용은 옳습니다.
마지막으로 parr + j가 parr의 j번째 배열의 첫 번째 원소를 가리키는 포인터를 가리키는 포인터 즉 이중 포인터가 여기에 어떻게 적용되는지 궁금합니다. parr + j가 왜 배열의 j번째 배열의 첫 번째 원소를 가리키는 포인터가 아니라 그 포인터를 가리키는 포인터이며 과정이 어떻게 다른지요..ㅠㅠ 이해가 가지 않습니다.
: parr + j
는 j
번째 배열의 첫 번째 원소를 가리키는 포인터가 아니라는 표현은 옳지 않습니다. parr + j
는 j
번째 배열의 첫 번째 원소를 가리키는 포인터가 맞습니다.
다만, int* parr[] = {arr0, arr1};
으로 선언된 parr
배열 자체의 자료형이 int* []
즉, 포인터의 배열이고, 배열의 이름인 parr
은 배열의 첫 번째 원소의 주소를 가리키는 포인터이므로, parr
은 이중 포인터가 되는 것입니다.
마찬가지로, parr + j
는 배열의 j
번째 배열의 첫번째 원소를 가리키는 포인터의 포인터입니다.
질문해주신 내용에 대한 개인적인 의견을 보태보자면, 강의 10.4 포인터와 배열 과 10.9 포인터 연산 총정리 의 복습을 추천드려봅니다. 해당 강의들에서 교수님의 설명이 질문자님께 보다 많이 도움되실 것 같습니다.
정말 정성스럽게 답변해주셔서 감사합니다..ㅠㅠ 말씀해주신 부분 꼭 다시 복습하겠습니다!! 마지막으로 ((parr + j) + i))에서 궁금한 것이 있습니다. 말씀을 정리해보면 parr + j는 parr의 j번째 배열의 첫 번째 원소를 가리키는 포인터를 가리키는 포인터라고 하셨는데 이 과정을 좀 더 자세히 봐서 &parr[0]=&arr0[0], &parr[0]+1=&arr0[0]+1=&arr0[1]이라고 보면 안된다는 건 알겠지만 parr[0]+1을 하면 parr[1]을 가리키는 포인터가 되는 것과 어떤 차이인지 모르겠습니다.
안녕하세요, 답변 도우미 Soobak 입니다.
우선, 질문하신 내용 중 "parr[0]+1을 하면 parr[1]을 가리키는 포인터가 되는 것과 어떤 차이인지" 에서, 차이에 대해서 비교하고 싶은 대상이 어떤 것인지 정확히 언급해주지 않으셔서, parr + 1
과의 비교를 가정하고 답변드리겠습니다.
또한, 해당 게시판에서 사용하고 있는 Markdown의 문법에서 *와* 사이의 문자는 기울어짐 으로 자동 변환이 되어, 문자를 기울이는 것이 의도한 것이 아니라면, 따로 수동으로 변환해주어야 합니다.
따라서, 말씀하신 "마지막으로 ((parr + j) + i))에서 궁금한 것이 있습니다." 에서 ((parr + j) + i))
은 *(*(parr + j) + i))
에 대해서 질문을 주신 것으로 가정하고 답변 드리도록 하겠습니다.
질문자님의 질문에서, 맨 앞의 (
가 기울어져 있기 때문입니다.
즉, parr[0] + 1
과 parr + 1
의 차이점에 대해서 설명드리도록 하겠습니다.
parr[0] + 1
: parr[0]
은 arr0
입니다. 배열의 이름은 그 배열의 첫 번째 원소의 주소를 나타내는 포인터이므로, parr[0]
은 arr0
의 첫 번째 원소의 주소를 나타내는 포인터가 됩니다.
이 때, int* parr[]
으로 선언된 배열의 원소인 arr0
의 자료형은 int*
입니다.
따라서, parr[0] + 1
연산을 하게 되면, int
의 크기만큼 주소값이 증가하게 됩니다.
즉, parr[0] + 1
은 arr0
의 두 번째 원소인 2
의 주소를 가리키는 포인터가 됩니다.
parr + 1
: parr
은 포인터들의 배열입니다. 이 경우, int*
타입의 포인터들을 저장하므로, parr
자체는 int**
타입의 이중 포인터로 볼 수 있습니다. 이중 포인터인 이유에 대해서는 위 2개 질문에서의 답변에서 충분히 이해가 되셨으리라 생각됩니다. 따라서, parr + 1
을 수행하면, int*
타입의 크기 만큼 주소값이 증가하게 됩니다.
즉, parr + 1
은 포인터 배열 parr
의 두 번째 원소인 arr1
의 주소를 가리키게 됩니다.
결론적으로, parr[0]
의 자료형은 int*
타입의 포인터이며, parr
의 자료형은 int**
타입의 이중 포인터이므로, 포인터 연산의 결과가 달라지는 것입니다.
간단히 요약하면, 각 포인터의 자료형이 다르기 때문에 연산의 결과가 달라지는 것으로 이해하시면 좋을 것 같습니다.
C언어에서 포인터 개념은 처음에는 낯선 개념이지만, 한 번 이해를 하고 나면 어렵지는 않은 개념 같습니다.
이 과정에서, 인내심을 갖고 꼼꼼히 하나 하나 따져보고, 직접 각 포인터 변수들을 출력해보시는 과정이 큰 도움이 되는 것 같습니다.
질문자님께서 혼동하시고 계신 것 같은 개념인 배열과 포인터의 관계, 포인터 연산, 이중 포인터와 다차원 배열 등과 관련된 섹션 10 배열과 포인터 에서 관련 내용들을 복습하시고, 스스로 다시 한 번 정리를 하신 후 질문해주시면 질문자님께서도 더 명확하게 스스로가 혼동하시는 부분에 대해서 알 수 있으실 것 같으며, 저도 더 양질의 정확한 답변을 드릴 수 있을 것 같습니다.
제 개인적인 생각으로, 강의에서의 교수님이 설명이 기초적인 원리와 동작 방식 등 '원리' 에 대해서도 설명해주시기 때문에, 질문자님의 학습에 더 도움이 되실 것 같기 때문입니다. 원리를 바탕으로 이해를 하는 것이, 단순히 암기를 하려 하는 것 보다 양질의 학습을 할 수 있는 것 같습니다. 또한, 제 답변보다 훨씬 명쾌하게 정리를 잘 해주신다는 것도 이유 중 하나입니다.
질문자님의 이전 질문들에서 '복습' 중이시라고 하셨기에 의견을 보태보았습니다.
최대한 자세히 설명드리고자 노력해보았으나, 만약 아직 이해가 어려우시다면 편하게 추가 댓글 남겨주세요.
마지막 답글에서
"따라서, parr + 1 을 수행하면, int* 타입의 크기 만큼 주소값이 증가하게 됩니다." 라고 하셨고
"parr[0] 의 자료형은 int* 타입의 포인터이며, parr 의 자료형은 int** 타입의 포인터이므로, 포인터 연산의 결과가 달라지는 것입니다." 라고 하셨는데
parr의 자료형이 int**인데 +1을 수행하면 int* 타입의 크기만큼 주소값이 증가하는 것을 어차피 c언어에서는 포인터 변수의 크기는 int*든 float*든 뭐든간에 동일(운영체제에 따라 바뀌기는 하지만)하기때문에 상관없다라고 봐도 될까요?
감사합니다.
안녕하세요, 답변 도우미 Soobak 입니다.
네, 말씀하신 내용이 맞습니다.
포인터 변수 자체의 크기와 포인터 연산 시 주소값이 변하는 크기는 별개의 개념임을 강조하기 위해 조금 혼동스러운 답변을 하고 말았네요.
따라서, parr
이 자료형이 int**
일 때, parr + 1
연산은 int*
타입의 포인터 크기 만큼 주소값이 증가한다라고 말씀드리는 것보다, 시스템에서 1
개의 포인터 변수 크기만큼 주소값이 증가한다고 정정드리는 것이 더 정확한 것 같습니다.
올바른 추측으로 좋은 지적을 해주셔서 진심으로 감사드립니다.
말씀해주신 부분에서 parr이라는 포인터 자체가 가리키는 것은 parr[0]-->arr0[0]인가요?? parr[0]이 arr0[0]을 가리키고 parr[1]은 arr1[0]을 가리키는 것은 이해했으나 parr이라는 포인터가 자체적으로 가리키는 것은 무엇인지 궁금합니다. 예를 들어 arr0이라는 배열 이름 자체는 arr0[0]을 가리키듯이요.. 또한 위에서도 말씀드렸었지만 parr[j]+i가 parr의 j번째 배열의 i번째 원소를 가리키는 과정과 *(*(parr + j)+i)가 j번째 배열의 i번째 원소를 가리키는 과정이 어떻게 다른지 구체적으로 알고 싶습니다. parr[j]가 parr의 j번째 배열의 첫 번째 원소를 가리킨다면 parr + j는 parr[0] 즉 parr의 첫번째 배열을 가리키는 포인터에 j를 더해 parr의 j번째 배열에 접근하는 것인가요..? 마지막으로 parr + j가 parr의 j번째 배열의 첫 번째 원소를 가리키는 포인터를 가리키는 포인터 즉 이중 포인터가 여기에 어떻게 적용되는지 궁금합니다. parr + j가 왜 배열의 j번째 배열의 첫 번째 원소를 가리키는 포인터가 아니라 그 포인터를 가리키는 포인터이며 과정이 어떻게 다른지요..ㅠㅠ 이해가 가지 않습니다.