noname02

포인터, 포인터 연산, 배열, 2차원배열, 다차원배열 완벽히 이해하기. 본문

Study/C

포인터, 포인터 연산, 배열, 2차원배열, 다차원배열 완벽히 이해하기.

kimtaku 2015. 5. 13. 03:24


1. 포인터


프로그래밍을 하면 변수를 생성하죠. 변수의 값은 메모리에 저장이 됩니다.

메모리의 최소단위는 1byte입니다. 그리고 각 메모리는 주소값을 가지고 있습니다.


int a=10;


을 선언했다고 하면, 10이라는 값을 저장하고 10이 저장된 메모리공간에 a라는 이름을 붙여줍니다. 그리고 이 메모리 공간은 int형입니다. 즉 4byte의 크기를 갖게 되는거죠


0000

a

(int)

0001

0002

0003


즉 이렇게 되있는겁니다. 왼쪽은 주소값이며(편의상 정의한 주소값입니다) 오른쪽은 변수의 이름입니다. a라는 변수는 int형이기 때문에 4byte크기를 할당받았죠.


주소값을 반환하는 연산자인 &를 아실겁니다. 위와 같은 상황에서 


printf("%d",&a)

이렇게 a의 주소값을 출력하라고 하면 0000이 나올겁니다. 통상 변수의 주소값은 변수가 시작되는 첫번째 메모리의 주소를 나타냅니다.


그래서 자료형이 중요합니다. a라는 변수에는 단순히 a라는 값만 들어있는게 아니라, 자료형인 int의 정보도 들어있다고 생각하시면 됩니다. 그래서 a를 출력하라고 하면 a는 int형이니까 4byte에 걸쳐서 저장된 값을 출력하는 것입니다.


int a=10;

int b=20;


이렇게 선언하면



0000

a

(int)

0001

0002

0003

1000

b

(int)

1001

1002

1003


이렇게 저장을 합니다.

그래서 


printf("%d",a);는 0000부터 0003까지 출력하는 것이고

printf("%d",b);는 1000부터 1003까지 출력하는 것입니다.

물론 a에는 주소 0000부터 0003에 걸쳐서 10이라는 값이 아래와 같이 2진수로 저장되어 있습니다.


00000000 00000000 00000000 00001010 -> 2진수로 10입니다.

(0003값) (0002값) (0001값) (0000값)


이게 바로 자료형을 정의하는 이유입니다.

컴퓨터가 메모리 주소 어디부터 어디까지 출력하는것을 구분짓기 위함이죠.


그럼 포인터는 무엇일까요?

포인터는 포인터라는 이름에도 나와있듯이 무언가를 가리키는 역할을 합니다.

그 무언가는 바로 메모리입니다. 그러니까 포인터는 메모리를 가리키는 변수입니다.

그래서 포인터는 변수의 값으로 메모리의 주소값을 갖습니다.

메모리가 어딨는지 알아야 메모리를 가리킬 테니까요. 

포인터도 변수니까 당연히 메모리 공간을 가져야합니다.

포인터는 4byte의 메모리 공간을 갖습니다. 이 4byte 공간에 주소값을 저장하는 것입니다.


포인터는 간접접근연산자인 *을 사용해서 나타냅니다.

&는 a && b로 사용하면 논리연산자가 되지만 변수 앞에, &a와 같은 형식으로 사용하면 주소값을 반환하는 연산자가 되듯이, *도 a * b로 사용하면 곱하기가 되지만 변수 앞에 *a와 같이 사용하면 간접접근연산자가 되는 것입니다.


그래서 포인터를 정의할 때, int *a; 와 같은 형식으로 정의합니다. 정의할 때 int* a, int * a, int *a 어떤 형식이든 상관없습니다. 다만 의미를 살려본다면 int* a가 어쩌면 이해하기 편할 수도 있습니다. 문법대로라면 *는 변수앞에 왔을 때 간접접근연산자가 되니까 int *a가 맞는 표현인 것 같기도 합니다. 그리고 실제로 많은 사람들이 이렇게 사용합니다. 

그런데 어째든 포인터도 변수이며, 그 변수의 자료형이 int* 즉 int형 포인터다 라는 의미를 생각해 본다면 int* a; 로 정의하는 것도 나쁘진 않은 것 같습니다.

어째든 포인터를 정의할 때에는 *를 사용해서 정의한다는 것은 변함없습니다.


포인터 변수에는 주소값이 들어간다고 했습니다. 따라서 포인터 변수에 값을 넣을 때에는 주소값을 넣어줘야 합니다.


int a=10;

int *b = &a; 


이렇게 정의하죠. &는 변수 앞에 쓰이면 주소값을 반환하는 연산자입니다. 

위와 같은 표현은 아래의


int a=10;

int *b;

b = &a;


와 같습니다. 여기서 중요한 점은 정의할 때 *를 변수에 붙여서 정의했으니 마치 변수를 사용할 때 *를 사용해야만 될 것 같지만 *는 그저 연산자일 뿐이며, 정의할 때에 *를 넣는 이유는 해당 변수를 포인터 변수로 정의한다는 의미로 넣는 것입니다. 그래서 int* b, int * b, int *b 어떤 방식으로든 정의해도 되는겁니다. 단지 포인터 변수임을 나타내기 위해 *를 사용했다는 것입니다. 그래서 포인터변수라는 의미를 살려본다면 int* a; 가 이해하기가 더 쉬울 수도 있다고 한 것입니다. int형 포인터니까요.


그럼 포인터가 자료형을 갖는 이유는 뭘까요?

그것은 포인터 변수로 자료를 출력하기 위함입니다.


int a=10;

int *b;

를 생각해 봅시다. 이는 메모리에 저장될 때


0000

a

(int)

0001

0002

0003

1000

b

(int*)

1001

1002

1003


와 같이 저장됩니다. 즉 a는 int형을 나타내고, b는 int형 포인터를 나타냅니다.


b = &a;


를 하게 되면 b의 메모리공간에 a의 주소값이 저장이 됩니다.


printf("%d",b); 

위의 값은 0000이 출력될 것입니다. 위에서도 언급했듯이 포인터도 변수이며, 간접접근연산자가 없기 때문에 b에 저장된 값이 그냥 출력되기 때문입니다. b에는 a의 주소값이 들어있으며 a의 주소값은 a공간의 첫번째 메모리 주소값이기 때문에 0000이 출력된겁니다.


그럼 간접접근연산자인 *를 사용하면 어떻게 될까요?


printf("%d",*b); 는 10이 출력됩니다.


*b는 b에 저장된 값의 주소로 접근하라는 의미입니다.

현재 b에는 a의 주소값이 들어있으므로, *b는 a의 주소인 0000으로 접근합니다. 그리고 여기서 b가 int형 포인터였기 때문에 int의 값인 4byte를 출력하는 것입니다. 즉 0000에 접근해서 int형만큼인 0000~0003까지 출력한겁니다.

만약


int a=10;

char *b=&a; 로 했다면 


printf("%d",*b)를 해도 여전히 10이 정상적으로는 출력됩니다. 다만 이 경우에는 *b는 char형 포인터이므로 1byte만큼 출력을 했는데, 10은 1byte로도 충분히 저장가능한 값이기 때문에 정상적으로 출력된 것처럼 보이는겁니다. 즉 0000의 값만 출력을 한 것입니다. 0001, 0002, 0003의 값은 출력되지 않았습니다. 그래서 만약 a에 char형 범위를 넘어서는 값이 저장되어 있다면 출력이 이상하게 될 것입니다.


이 정도면 포인터가 무엇인지 감이 잡혔을 거라 믿습니다.



2. 1차원 배열, 그리고 포인터 연산


이런 얘기를 들어보신적이 있으실겁니다.


"배열의 이름은 포인터다."


1차원 배열 int arr[3] = {0, 1, 2}를 생성했다고 해봅시다.

그러면 arr[0] = 0, arr[1] = 1, arr[2] = 2 이렇게 저장이 되어있을 것입니다. 이는 


0000

arr[0]

(int)

0001

0002

0003

0004

arr[1]

(int)

0005

0006

0007

0008

arr[2]

(int)

0009

0010

0011


와 같이 나타낼 수 있습니다. 

배열은 이렇게 변수를 메모리 주소 차례대로 저장합니다. 위에서 봤던 변수들은 메모리를 차례대로 저장하지 않습니다. 그런데 배열은 이렇게 차례대로 저장합니다. 이는 포인터 연산이라는 독특한 문법을 가능하게 해줍니다.

포인터 연산은 잠시 뒤에 나오니 여기서는 배열의 이름 arr이 갖는 의미를 한번 알아봅시다.


printf("%d",arr);


은 어떤 값을 나타낼까요? 코딩을 해보면 0000으로 나타납니다.

주소값이 나타나네요? 이는 arr이 포인터라는 뜻으로 해석됩니다. 주소값을 출력해줬으니, arr에는 주소가 저장되어 있다는 뜻이죠.

위에서 변수의 주소는 변수가 갖고있는 값의 첫번째 메모리 주소값을 나타낸다고 했습니다.

arr은 0000부터 0011까지의 배열이니, 0000으로 나타나는게 이상한 일이 아니죠.


arr이 포인터라고 했으니, *arr을 출력하면 뭔가 나올 것 같습니다.


printf("%d",*arr); 


얘는 0이 출력됩니다. 즉, arr[0]의 값이 출력됩니다.

즉 *arr == arr[0] 입니다.


C언어에서는 포인터 연산이라는 특별한 문법을 제공합니다.


arr+1을 하면 어떻게 될까요? 


printf("%d",arr+1);


애는 0004가 나옵니다. 1을 더했더니 arr포인터가 arr[0]에서 arr[1]을 가리킵니다. 

근데 사실 arr에 1을 더해서 arr[0]을 가리키던걸 바꿔서 arr[1]을 가리키는 것이 아닙니다. arr이 0000을 가리키는 주소였다가 1을 더해서 0004를 가리키는 주소가 되었는데, 0004에 arr[1]이 있었을 뿐입니다.

그리고 이렇게 포인터에 상수를 더했더니 주소값이 4씩 변하는데 이는 arr이 int형 배열이기 때문입니다. 만약 char형 배열이었다면 주소값은 1씩 변했을 겁니다.


즉 이게 바로 포인터 연산입니다. 따지고 보면 주소값에다가 상수1을 더한다는건 말이 안되는 덧셈입니다. 속도에다가 온도를 더하는 꼴이죠. 주소값과 상수는 다른 개념입니다. 단지 지금까지 포인터에 저장된 값을 표현했을 때에는 주소값을 상수로써 표현해줬을 뿐이지, 주소값을 상수값이라고 생각하면 안됩니다. 마치 우리가 살고있는 집 주소에 1을 더하면 옆집이 된다는 이상한 덧셈인 것이죠. 

하지만 포인터 연산은 포인터의 자료형이 정해져있기 때문에, 그렇게 하자고 약속한 문법인겁니다.

1차원 int형 배열에서는 배열의 이름에 1을 더하면 주소값이 4씩 증가하는 것으로 약속한겁니다. int형이기 때문에 4인겁니다. char이었으면 1씩 증가합니다.


그리고 위에서 arr에 1을 더해서 arr[0]을 가리키던걸 arr[1]로 가리키는 것으로 바꿨다는게 아니라 그저 arr[1]이 거기에 있었기 때문에 그렇게 됐다고 표현을 했는데 이 말을 좀 더 쉽게 이해하자면, arr은 현재 arr[2]까지밖에 없습니다. 즉 arr배열은 0000부터 0011까지밖에 없죠. 그럼 만약에 *(arr+3)을 하면 어떻게될까요? 이는 arr[3]이라고 생각 할 수 있는데, 위에서도 얘기했듯 arr은 현재 arr[2]까지밖에 없습니다. arr+3은 포인터연산에 의해 주소값 0012를 가리키게 되는데, 0012에는 현재 아무 값 초기화시켜주지 않았습니다. 따라서 쓰레기값인 더미값이 출력됩니다. 즉 그냥 의미없는 이상한 값이 나오는 겁니다.

이 포인터연산에 대한 개념을 잘 숙지하셔야 합니다. arr에 1을 더해서 arr[0]을 가리키는걸 arr[1]로 가리키는 것으로 바꾼게 아닌 그냥 arr[1]이 거기 있었다 라는 개념을 잘 숙지하셔야 이 뒤에 나오는 2차원 배열에서 이해하기가 수월합니다.


여튼 위에서 했던말이 기억나실겁니다. "배열의 이름은 포인터이다."


이제 이게 무엇을 뜻하는지 아실겁니다.




3. 2차원 배열


1차원 배열이 아닌 2차원 배열을 알아보겠습니다.


int arr[2][3] = { {1, 2, 3}, {11, 12, 13} };


이렇게 정의하면 이는


0000

arr[0][0]

(int)

0012

arr[1][0]

(int)

0001

0013

0002

0014

0003

0015

0004

arr[0][1]

(int)

0016

arr[1][1]

(int)

0005

0017

0006

0018

0007

0019

0008

arr[0][2]

(int)

0020

arr[1][2]

(int)

0009

0021

0010

0022

0011

0023



이런 구조입니다.

배열이므로 주소값이 연속되게 저장되었습니다.


printf("%d",arr); 의 출력값은 0000일 것입니다. 그럼, arr+1의 출력값은 얼마일까요? 0004일까요?

2차원 배열에서는 1차원 배열에서의 포인터연산과 좀 다른 문법이 적용됩니다.


printf("%d",arr+1); 의 출력값은 0012입니다. 즉 arr[0]에서 arr[1] 배열쪽으로 넘어온 것입니다.

이건 규칙입니다. 왜? 라고 물어봐도 소용 없습니다. 

즉 1차원 배열과는 다르게 arr에 1을 더하면 주소값이 12씩 증가합니다. 12가 증가한 이유는 arr[i][j]를 행렬이라고 생각했을 때, 한 행에 원소가 3개씩, 즉 int 자료형인 4byte가 3개인 12byte 단위로 묶여있기 때문에 12씩 증가한겁니다.

만약 arr[2][5]라면 arr+1은 20씩 증가할 것입니다.arr[0]에서 arr[1]로 가기까지 5개의 int형 배열을 지나야하기 때문입니다. (arr[0][0] 부터 arr[0][4]까지)


그럼 이번에는 arr[0]을 출력해봅시다.


printf("%d",arr[0]); 의 출력값은 어떨까요? 왠지 arr[0][0]의 값인 1이 출력될 것 같이 느껴질 수도 있습니다만 arr[0] 역시 0000이 출력됩니다. 

헉! 2차원 배열에서는 arr[i]값도 포인터였습니다.


그럼 *arr을 출력하면 어떻게 될까요?


printf("%d",*arr); 의 값은 1일까요? 아닙니다. *arr도 0000이 출력됩니다! arr도, *arr도 모두 주소값이 출력됩니다.

그렇습니다. 1차원 배열에서는 배열의 이름이 포인터였다면, 2차원 배열에서는 배열의 이름은 더블포인터입니다.


더블포인터는 간단합니다. 포인터 변수의 주소를 담고있는게 더블포인터입니다.


즉 더블포인터는 포인터를 가리키며, 포인터는 변수를 가리키고 있는 관계입니다.


int a = 10;

int *b = &a;

int **c = &b;


즉 더블포인터는 포인터의 주소를 가지고 있습니다.


0000

a

(int)

0001

0002

0003

1000

b

(int*)

1001

1002

1003

2000

c

(int**)

2001

2002

2003


와 같은 형태입니다.

위에서도 얘기했듯 배열이 아니면 연속되게 저장되지 않습니다. 더블포인터 역시 마찬가지겠죠.

배열만이 주소값이 연속되게 저장됩니다.


printf("%d",b) 는 0000을 출력하듯(a의 주소값)

printf("%d",c)는 1000을 출력합니다. (b의 주소값)


printf("%d",*b)는 a의 값인 10을 출력했습니다. 

그렇다면 *c는 무엇을 출력할까요? 예상하시는 바로 그것입니다.

printf("%d",*c)는 b의 값인 0000을 출력합니다. (b에 저장되있는 a의 주소값. 즉 b의 값)


그렇다면 **c는 무엇을 출력할까요? 혹시가 역시입니다.

printf("%d",**c)는 a의 값인 10을 출력합니다. 즉, *(*c)인데 *c == b 였습니다. 즉 *(b)가 된 것이고, *b는 a를 출력하는 것입니다.


그러니까 *연산자는 이렇게 생각할 수 있습니다.

"만약 주소값 앞에 *가 붙는다면, 해당 주소에 있는 값을 *가 사용된 변수의 자료형 크기만큼 출력시킨다."

그러니까 


int a=10;

printf("%d", *(&a));


를 출력하면 정상적으로 10이 나옵니다. &가 a의 주소값을 반환했고, *는 그 주소값으로 이동을 한 효과가 나타나는겁니다. *는 a의 자료형만큼 출력이 되었겠죠. 포인터 변수가 아니었지만 마치 포인터처럼 사용한겁니다.



그럼 다시 2차원 배열로 돌아오겠습니다.

arr은 0000이 출력되고, *arr 역시 0000이 출력됩니다. arr, *arr이 모두 주소값이 출력된 것으로 보아 arr은 더블포인터라고 생각할 수 있습니다. 그러면 논리적으로 생각했을 때 *arr은 arr[0]을 나타낸다고 볼 수 있습니다.

위에서 arr이 0000을 출력시켰는데 arr+1은 0012를 출력시켰으니까 말이죠.

그러니까 이런 구조인 것입니다.



arr

(int**)

arr[0]

(int*)

arr[0][0] (int)

arr[0][1] (int)

arr[0][2] (int)

arr[1]

(int*)

arr[1][0] (int)

arr[1][1] (int)

arr[1][2] (int)



arr은 arr[0]의 주소값을 나타냈으니, arr+1은 arr[1]의 주소값을 나타낼 것입니다. 이는 포인터 연산에 의해 그런것이며, 그냥 그렇게 문법을 정의했기 때문에 그런것입니다. 즉 arr에 1을 더하면 주소가 12씩 증가합니다. (위에서 얘기했었지만 행렬의 행원소의 갯수*자료형byte 만큼 증가하기 때문에 이 경우에는 12씩 증가한 것입니다.)


arr[0]은 arr[0][0]의 주소값인 0000을 나타낼 것입니다. 그러면 arr[0]+1은 arr[0][1]의 주소값인 0004를 나타낼 것입니다.

즉 arr[0]에 1을 더하면 1차원 배열과 마찬가지로 주소값이 4씩 늘어난다는 소립니다.

그럼 *(arr[0])은 뭐가 나올지 감이 잡힙니다. arr[0][0]값이 출력됩니다.

arr[0]은 주소값이 0000이고 포인터이기 때문에 *arr[0]은 arr[0][0]의 값이 나오는 겁니다.


그러면 *(arr)[0]은 어떻게될까요? *(arr)[0]은 *arr[0]이나 똑같습니다. (arr)[0]이 arr[0]과 다를바가 없기 때문입니다. 따라서 *(arr)[0]은 arr[0][0]값이 출력됩니다. 간접접근연산자인 *와 배열키워드인 [] 중에서 []가 우선결합순위가 높습니다. 이는 c언어 연산자 우선순위에 나오는 내용입니다. 덧셈과 곱셈중에서 곱셈을 먼저 하듯, 간접접근연산자와 []가 있으면 []를 먼저 합니다.


그럼 (arr+1)[0]은 어떤값이 나올까요? 좀 복잡합니다.

arr은 주소값이 0000이었고, arr+1은 주소값이 0012였습니다. 그럼 arr+2는 주소값이 0024가 나올것입니다.

마찬가지로 arr[0]은 0000이었고, arr[1]은 0012였습니다. 그럼 비록 정의는 안되있겠지만 arr[2]는 0024가 나올것이라고 추측할 수 있으며 실제로 그렇게 나옵니다. arr[2]쪽은 정의가 안되어있지만, arr[2]는 포인터변수이고 포인터변수는 포인터연산이 되기 때문에 그런것입니다. 

그러니 (arr+1)[0]은 (arr+1)자체가 주소값 0012을 나타내니 [0]을 붙여봐야 어차피 arr+1 그대로일 것입니다. arr+0 == arr[0] 이기 때문입니다. 즉 arr+0 == (arr+0)[0] 의 주소값이 0000이니

arr+1 == (arr+1)[0] 역시 arr+1의 주소값이 나올 것입니다.


그럼 (arr+1)[1]은 어떤 값이 나올까요?

arr+1은 0012를 가리킵니다. arr[1]도 0012를 가리킵니다. (arr+0)[0]에서 (arr+0)[1]로 가면 주소값이 12가 증가했듯이, (arr+1)[0]에서 (arr+1)[1]로 가면 역시 주소값이 12가 증가할 것입니다. 따라서 주소값은 0024가 출력됩니다.


그러니까 2차원 배열에서는 이런겁니다. arr변수에다 1을 더하나, arr[0] arr[1]과 같이 []안의 값을 1씩 증가시키나 결과는 똑같습니다. 그러니까 arr+1을 하는것은 arr[1]과 같다는 소리입니다. 쉽게생각하면 [0], [1]도 그냥 arr에 값을 더해주는 것일 뿐입니다. arr[10]은 arr+10이고 arr[20]은 arr+20인겁니다.

(arr+2)[3]도 그럼 알 것 같습니다. 그냥 arr+5인겁니다. 즉 arr[5]의 주소값이 나타날것입니다.


그러면, *(arr+1) 과 *arr[1]은 같은 값을 나타낼까요?

그건 또 아닙니다.


그냥 arr은 더블포인터입니다. []가 붙어야 포인터가 됩니다. 단지 방금 했던 일은 포인터연산에 따른 주소값이 어떻게 바뀌냐를 알아본겁니다. 그게 더블포인터를 포인터로 바꿔주지는 않습니다.

어째든 배열의 이름에 []가 붙어야 포인터가 되며, [][]가 붙으면 그냥 변수가 됩니다.

따라서 *(arr+1)은 더블포인터에서 포인터값을 가리키는 결과가 나온 것이고, *arr[1]은 포인터값에서 변수값을 가리키는 결과가 나온 것입니다.


그럼 배열의 이름인 arr만으로 배열원소의 값을 어떻게 표현할까요? 간단합니다. 더블포인터니까 *가 2개 들어가있으면 됩니다.


**arr을 하면 arr[0][0]의 값이 나오겠죠.

**(arr+1)을 하면 arr[1][0]의 값이 나오겠네요.

그럼, arr[0][1]은 어떻게 표현할까요?

arr은 더블포인터니 **와 같이 *연산자를 2번 써줘서 원소를 가리키게 해야합니다.

그리고 arr[0][1]에 해당하는 0004의 주소값을 포인터연산으로 만들면 됩니다.

그럼 일단 arr로 arr[0]으로 만들고, arr[0]+1을 한 뒤 *를 사용해서 *(arr[0]+1)과같이 하면 arr[0][1]이 나오겠네요.


그러면 역순으로 한번 나가보겠습니다.

우리가 원하는건 arr[0][1]이니까

*(arr[0]+1) == arr[0][1]이고

여기서 arr[0]은 *arr이므로

따라서 *(*arr+1) 과 같이 하면 나올 것 같습니다.

실제로 나옵니다.


arr[1][1]도 한번 가리켜보죠.

arr[1][1]은 *(arr[1]+1)입니다.

arr[1]은 *(arr+1)입니다.

따라서 *(*(arr+1)+1)를 하면 arr[1][1]이 나오겠네요.

해보시면 알겠지만 실제로 그렇게 나옵니다.




이렇게 포인터와 배열, 그리고 포인터연산에 대해 정리해봤습니다.

사실 포인터는 정말 어떻게보면 안정성이 떨어지는 문법입니다.

위에서도 얘기했었지만 포인터 연산은 메모리주소값 자체를 바꾸기 때문에, 만약 초기화되지 않은 메모리에 접근할 경우 더미값이 출력될테고 이는 프로그램에 오류를 야기시키겠죠.


그리고 2차원 배열에서는 더블포인터에대한 포인터연산을 했는데, 여기서는 더블포인터에 1을 더할때마다 행의 주소가 하나씩 바뀌는 연산이 됐습니다. 행의 원소의 갯수에 따라서 값이 바뀌는 연산이 진행되었죠.

이는 2차원 배열이라는 특수한 경우이기 때문에 그렇게 된것입니다.

만약 


int a=10;

int *b=&a;

int **c=&b;



0000

a

(int)

0001

0002

0003

1000

b

(int*)

1001

1002

1003

2000

c

(int**)

2001

2002

2003



와 같은 경우, b나 c도 역시 포인터이기 때문에 포인터연산이 되며 b를 출력하면 0000이지만(a의 주소값)

b+1은 0004가 됩니다. 초기화되지 않은 메모리를 가리킵니다.

c는 더블포인터인데, c를 출력하면 b의 주소값인 1000이 나올것입니다. c+1을 하면 1004가 나옵니다. 배열이 아니라서 그냥 포인터연산이 자료형인 int형 크기만큼 된 것입니다. 


즉 포인터연산은 배열이 아니면 쓸모가 없습니다. 어쩌면 더 엄청난 프로그래밍을 하시는 분들이라면 뭐 어떻게 사용하는 방법이 있을지도 모르겠지만.. 기초문법에서는 쓸모가 없습니다.


그리고 이렇게 포인터연산은 잘못사용하면 기대하지 않은 더미값이 출력되는 일이 나타납니다.

그래서 JAVA에서는 이런 불안정한 포인터를 허용하지 않고, C#도 특수한 옵션으로 허용을 하지만 원칙적으로는 허용하지 않습니다. 프로그래머의 실수를 막겠다는 것이죠.


하지만 이런 포인터 개념은, 나중에 동적할당과 매우 밀접한 관계를 가지고 이렇게 프로그래머가 직접 메모리를 관리하는것이 잘만 사용하면 단점이 큰 장점으로 바뀌게 됩니다. 효율적으로 메모리를 관리할 수 있겠죠.

여기에서는 동적할당을 다루지 않았지만, 여튼 나중에 동적할당을 배우고 더 나아가 JAVA와 C#을 배우게 된다면 이 말의 의미가 어떤것인지 아실겁니다.


본문에서는 2차원배열까지 다루었지만, 다차원 배열 역시 2차원배열과 같은 원리임을 알면 직관적으로 이해가 가능할 것입니다. 그리고 실제로 3차원 이상의 배열은 잘 사용하지 않는다고 하네요.


그럼 이상으로 글을 마치겠습니다.

글을 쓰면서 저도 확실히 정리되는 느낌이네요 ㅎㅎ


'Study > C' 카테고리의 다른 글

함수포인터 이해하기  (0) 2015.05.09
문자열 변수 선언  (0) 2015.04.04
자료형 접미사  (0) 2015.04.04
const 키워드  (0) 2015.04.04
static 변수  (0) 2015.04.04
Comments