2차원 배열을 선언 후 함 수 이자로 넘겨줄 필요가 있는 경우
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
이렇게 선언한 배열은
int a[12]={1,2,3,4,5,6,7,8,9,10,11,12};
라기 보다는
int a[3]={int[4], int[4], int[4]}
이것과 같다고 볼 수 있다.
무슨말이냐
이중 배열의 주소체계는 이러하다.
a자체는 총 3개의 int[4]원소를 가진 배열의 시작주소를 나타내고
a의 첫번째 원소 a[0]( *(a+0)과 같음 )=int[4]={1,2,3,4}
두번째 원소 a[1]=int[4]={5,6,7,8}
세번째 원소 a[2]=int[4]={9,10,11,12}가 된다.
따라서 a[0]의 주소와 a[1]의 주소는( 4byte(int 크기)*4=)16byte 차이가 난다.
a[0]은 총 4개의 int원소를 가진 배열의 시작주소를 나타내므로
a[0]의 첫번째 원소 a[0][0]( *(*(a+0)+0)과 같음 )=int={0}
두번째 원소 a[0][1]=int={1}
...
과 같다.
따라서 a[0][0]의 주소와 a[0][1]의 주소는 4byte차이가 난다.
이를 코드로 확인해보면
>>결과
이케 된다.
a의 주소와 그 다음 주소 값을 확인해보면 16진수로 48byte(3*(int(4byte)*4))=(0x30) 차이가 나는 것을 알 수 있다
a의 첫번째 원소(*a=a[0])의 주소값과 두번째 원소(*(a+1)=a[1])의 주소값은 16byte(int*4)=(0x10) 차이가 나고
a[0]의 첫번째 원소(*(*(a+0)+0)=a[0][0])의 주소값과 두번째 원소(*(*(a+0)+1)=a[0][1])의 주소값은 4byte(int)=(0x04) 차이가 난당
여기서 나는 헷갈렸던게 a는 int 포인터 타입의 변수인줄 알았다.
즉 a는 배열을 메모리에 할당하고 그 주소값만 가지고 있는 변수라고 생각했는데
그게 아니라 그냥 그 배열 자체를 나타내는 변수였던 것이다
이게 무슨 말이냐면 변수를 사용할 때
int a=4;
이케 선언하면 4라는 값을 메모리에 할당한 후 그 메모리의 주소값을 나타내는 애가 a이다.
a라는 변수를 사용해서 4를 할당해준 메모리에 접근할 수 있는 것이다.
(이게 변수를 만들어 주는 이유. 우리가 항상 메모리 주소를 기억하고 사용하기 불편하므로 변수를 사용해서 쉽게 사용하는 것)
a는 4를 갖고 있는 메모리자체라고 볼 수 있음
나는 이걸 a는 4라는 값을 갖는 int 자체를 나타내는 변수 라고 표현했다.
근데 포인터형 변수는 그 메모리 자체를 나타내는 변수가 아니라 메모리 주소값만을 갖는 변수이다
int* b=&a
라고 선언하면 a는 메모리의 주소값을 포인터형 변수인 b에게 알려주는 것이다.
그림을 보면 a는 4값을 담고 있는 메모리이고 b는 4를 담고 있는 메모리 주소를 담는 메모리이다.(아...쓰면서도 헷갈려)
b는 0xb4라는 주소를 따로 갖는 4를 담는 메모리와는 별개의 변수이지만 a는 4를 담는 메모리 자체인것이다.
부디 나중에 보면서도 이해되길....
다시 처음으로 돌아가서 왜
함수에서
int a[3][4]로 선언한 배열을
int** arr 로 받으면 에러가 나는가?
a는 배열을 가지고 있는 메모리 자체를 나타내는 변수고
arr은 정수값을 갖는 메모리를 찾아가기 위한 주소 값을 갖는 포인터형 변수이기 때문이다.
arr로 넘어오는 것은 a의 주소값인 0x00 이다.
arr은 0x64번지에 메모리를 할당해주고 0x00이라는 주소값을 담고 있는 변수!
또 arr은 이중 포인터이기 때문에 처음에 0x00을 따라간 후에 0x00번지에 담겨 있는 값도
8byte의 주소값(64bit에선 8byte, 32bit에선 4byte)으로 인식을 한다.
0x00번지부터 0x08번지까지에 있는 binary값을 읽은 다음 그값을 16진수의 주소(0x21)로 인식을 하는 것이다.
그래서 이를 따라간 다음 int 값으로 읽기 위해 4byte의 binary 값을 읽으면 값이 이상해진다.
(간단하게 그림을 나타내기 위해 정수값을 주소값으로 변환하는걸 그림에서는 야매로 0x21로 나타냈는데
실제 컴퓨터에서는 정수값 1과 2를 연달아 binary값으로 읽은 다음 16진수 주소값으로 바꾸면
0x0000000200000001이 된다.
정수값 1은 0x00000001로 저장 되고 정수값 2는 64bit에서는 0x00000002로 저장되는데
little endian으로 8byte를 읽으면서 0x0000000200000001 이 되는 것이다)
그래서 제대로 넘겨주기 위해선
>>결과
int(*arr2)[4]로 넘겨준 arr2를 보면 0x000000020bff8fbd0번지에 할당되어
a의 주소값 0x000000020bff8fbf8을 갖고 있는 포인터형 변수라는 것을 알 수 있다.
따라서 arr2의 사이즈는 8byte (64bit운영체제에서의 주소크기)이고 arr2가 가리키는 것은(0x000000020bff8fbf8번지에 있는 데이터=(*arr))
int[4]라는 것을 알려줬기 때문에 *(arr2)의 사이즈는 16byte라는 것을 알고 있다.
그리고 *(arr)이 가리키는 데이터(0x000000020bff8fbf8번지에 있는 데이터=*(*arr))는 int형이니까 4byte로 출력된다.
(arr2=*arr2=*(*arr2)인 이유는 arr2가 이중 배열을 가지고 있는 메모리 자체를 나타내는 변수 이기 때문. 위에 설명해놨다)
'Programming Language > C언어' 카테고리의 다른 글
data를 16진수로 출력하기 (0) | 2016.07.29 |
---|---|
문자열 함수 (0) | 2016.04.15 |
char 배열에 담겨져 있는 string 비교 (char array comparison) (0) | 2016.02.22 |
visual studio 단축키 (0) | 2016.02.21 |
현재 디렉토리의 파일리스트/파일타입/파일정보(실행가능유무) 출력 (0) | 2016.02.11 |