출처 : cafe.naver.com/laydigital/
안녕하세요, 갑자기 궁금해서 질문을 쓰게 됐습니다.
2주정도 C언어 관련 문법들을 살펴보고 정리하다가, 이차원 배열 arr[][] 과 포인터에 관한 내용중 그 관계과 궁금하고 명확하게 설명한 자료가 없어 질문드립니다. 물론, 사용하는건 외워서라도 할수있지만 저의 경우 외워서 하는건 영 마음에 않들어서 이해를 통해 활용하는걸 가장 좋아하는 스타일이라 이렇게 질문 드려봅니다.
질문 내용은 다음과 같습니다.
제가 자료들을 토대로 정리하여 이해한 내용은 1차원 배열의 경우 int arr[]로 선언햇을경우 기본형은 arr= int(*)형 혹은 int* 형은 바로 int *ptr, ptr =arr 이경우 ptr은 int * 형 , arr도 int(*),int* 형 그러므로 서로의 포인터형이 같은 형이므로 대응된다 여기까지는 참 이해하기 쉬운 부분이였습니다.
그런데 문제는 2차원 배열로 선언할 경우 입니다.
책, C/C++언어 관련사이트에선 다음과 같이 int arr[x][y] 일경우 int **ptr은 arr에 대응되지않고 다음과 같이 포인트배열을 선언
하여 그 주소값을 대응시켜 사용한다고 설명합니다. int arr[2][3]; 일경우 대응되는 포인터는 int(*ptr)[3]입니다. 물론 그림과
주소 address 등을 첨부하여 잘 정리 되어있습니다 그런데 어디에도 (*prt)붙이는 이유에 대해서 설명이 없습니다.
여기서 어떤 블로그자료에선 int **ptr, prt= (int**)arr 이런식으로 형변환을 시켜서 거의 반 억지로 주소를 배분해서 쓰기도 하던데 저 경우 해본 결과 다음과 같이 할경우 어이없는 일이 벌어집니다.
int a=1 ; ptr = &a; ptr = (int*)a; 이렇게 하면 주소를 변수a의 상수값 1을 가지고 *ptr 값을 출력해보면 unInitialized error가
발생합니다 당연히 address 1에는 어떤값도 정의 하거나 초기화하지 않았는데 저렇게 어거지로 집어넣으면 변수의 형이 맞기때문에 값은 들어갑니다.. 너무 억지스러워서 쓰기도 참 조심스러운 방법입니다..
제 경우 너무 궁금해서 일부러 visual studio에 컴파일 에러를 만들어서 현재 내가 전달할려는 변수의 수형이 어떤형인지를
일일 확인해 보았는데요.
1차원 배열의경우 int arr[], arr = int(*), int*, &arr=int(*)[]
2차원 배열의 경우 int arr[][], arr=int(*)[], &arr=int(*)[][]
1차원 포인터배열 int(*prr)[], prr=int(*)[], &prr=int(**)[]
2차춴 포인터배열 int(*prr)[][], prr = int(*)[][], &prr=int(**)[][]
다음과 같이 형을 인식하여 prr = arr 은 서로 형이 일치하여 바로 대응 됩니다. 그래서 int arr[2][3], 은 int(*prr)[3]에 바로
대응하여 prr = arr; 이런식으로 바로 바로 대응하여 prr[1][1] = arr[1][1] 은 같은 값을 가리키게 됩니다.
여기까지는 다소의 수고가 들어도 확인 해보고 살펴볼수있는 충분한 방법은 많았습니다 하지만 정작 왜? 2차원 배열의포인터를 선언할 경우 (*ptr)안에 포인터를 선언하지는 어디에도 설명해주는 곳이 없습니다 제가 제일 궁금한건 ( ) 안에 왜 포인터를 선언해 주어야 하는것입니다, 어떤 수식, 형의 상관 관계 때문인지 ..이문제때문에 몇일을 고생했지만 마음에 드는 설명을 해주는곳이 없어서 그냥 묻어 두었다가 C언어 관련 질문은 어느때건 해도 된다고 하신 얘기가 기억나서 질문을 써봤습니다.
가장 근복적인 이해가 부족하면 활용하는데 큰 장애가 될 수있어서 다소 장황하지만 이렇게 질문드리오니 설명의 양이 많아
여건상 힘드시다면 관련 사이트를 알려주시면 참고하여 살펴보겠습니다. 사용법은 그냥 쓰면 되지만 분명 활용에 대한 제약은 굉장히 클거라고 느낍니다. 제경우 이쪽분야는 아니지만 제가 일한 분야는 기구, 금형, 자동차(Exterior), layout 설계인데요 제가 공부해온 경험으로 비추어보면 복잡한 논리와 이론도 가장 밑바탕에서 시작되는 충분한 이해를 기반으로 사용해야 아무리 어려워 보이는 설계나 기능 구현도 쉽게 할 수 있었거든요... 긴 질문 읽어주셔서 감사합니다 __)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int arr[2][3];
arr[0]는 3개짜리 int 의 첫번째 주소(offset=0)
arr[1]은 3개짜리 int 의 두번쨰 주소(offset = 3 * 2) / int 가 2바이트 인경우
array선언시 arr는 컴파일시 해당 arry의 시작주소가 되는 상수입니다.
변수로
arr[0]에 대응할려면
*p 가 되야죠.
*p = p[] 와 같으므로
arr[]와 같은 레벨은 *p
int (*p)[3]; 이 int arr[2][3]의 속성과 같죠.
설명하기가 어렵네요.
복잡한 포인터 사용을 할떄는 저도 다시 찾아보고 사용합니다.
사용할 일이 별로 없어서....
왜 *p로 선언했는지를 궁금해 하신건가요?
선언은 p 라는 변수 하나(array 가 아닌) 선언하는 겁니다.
p 를 선언하는데
*p : p 가 가리키는 곳은
(*p)[3] : 3개짜리 array에 대한 주소이며
int (*p)[3]: 각 3개의 방은 int 타입이다.
()를 하지 않으면 우선순위떄문에 다르게 해석이 됩니다.
int *arr[3];
은 3개의 element를 가지는 array를 선언하며 각자는 int 가 들어있는 주소를 저장합니다.
(*ptr)[] 는
ptr 변수 하나를 선언하며, 그 변수가 가리키는 곳은 array([] 형태 단위) 입니다.
int arr[2][3];
이건
arr[2][3] : arr로 시작주소를 가지는 2x3(6개)의 변수를 선언하며 해당 변수는 int 타입니다.
int (*arr[2])[3];
변수를 2개(array)로 선언하는데, 그 각각의 값은(*arr[])는 3개짜리 integer 수형에 대한 수소입니다.
array : 0 1 2 3 4 5 6
^ ^
arr[0] arr[1]
^ ^
p p+1
int arr[2][3] => int (arr[])[3] => int (*arr)[3];
arr는 변하지 않는 상수이긴 하지만.....
No comments:
Post a Comment