지난 장에서는 Pointer에 대해서 배웠습니다.
이번 장에서는 Pointer의 활용에 대해서 계속 이어나가겠습니다.
지난 장 마지막에 이야기 했듯이,
왜 복잡하게 변수를 선언하고,
선언된 변수의 주소를,
또 다시 선언된 Pointer 변수에 집어 넣는지...
에 대한 확실한 답을 가지고 있어야 합니다.
이번 장에서는 그 이유 중 한가지인 array(배열)과 연관지어서
이야기를 풀어나가도록 하겠습니다.
[Array]
배열은 변수 이름과 index를 통해서 access 가능한 형태의 변수이며,
data base 처리 및 sw를 이용한 신호처리에서 특히나 많이 이용됩니다.
Array(배열)은 다음과 같이 선언이 됩니다.
int a[100];
int b[100];
int c[100];
선언된 int 배열은 다음과 같이 [] 안에 있는 index를 기준으로 access됩니다.
단, 이때에 index는 zero-base, 즉 0부터 시작 합니다.
for(n=0; n<100; n++) a[n] = n;
위의 예제에서 배열 a는 memory 상에서 다음과 같이 자리를 잡게 됩니다.
(base address는 compiler/linker가 알아서 잡아줍니다.)
--------------------------------------
a[0] | 0 | base address
--------------------------------------
a[1] | 1 | base address + 4
--------------------------------------
a[2] | 2 | base address + 8
--------------------------------------
a[3] | 3 | base address + 12
--------------------------------------
a[4] | 4 | base address + 16
--------------------------------------
....
--------------------------------------
a[99]| 99 | base address + 99x4
--------------------------------------
아래 예제에서, a[0]는 0을 반환하고, &a[0]는 base address를 반환하고,
그 다음도 순차적으로 증가하는 것을 확인 할 수 있습니다.
for(n=0; n<100; n++) printf("%x: %d", &a[n], a[n]);
배열은 이와 같이 동일한 형태의 data set을 하나의 대표 이름과 index로
처리가 가능하게 해줍니다.
이러한 배열을 function 내에서 처리하기 위해서
인자를 넘겨 줄때에 pointer가 사용됩니다.
예를 들어서,
배열 a와 배열 b를 순차적으로 각각 더해서 배열 c에 결과를 저장하는 함수를
구현해야 한다고 했을때에,
함수는 아래와 같이 배열 a,b,c의 base address를 인자로 받을 수 있습니다.
void func1(int *a1, int *a2, int *sum, int nMax)
{
int n;
for(n=0; n<nMax; n++)
sum[n] = a1[n] + a2[n];
return;
}
함수 func1에 배열들의 base address를 다음과 같이 넘겨주면,
func1은 실질적으로 a 와 b를 각각 더해서 c에 값을 저장하게 됩니다.
func1(&a[0], &b[0], &c[0], 100);
또한 &a[0]와 a는 같은 의미 이기 때문에 다음과 같이 호출될 수도 있습니다.
func1(a, b, c, 100);
두 가지 함수 호출 모두 동일한 결과를 나타냅니다.
이처럼, Array variable을 함수 내에서 처리하고자 할 때에는,
그 함수에 배열의 base address를 인자로 전달하며 처리하고,
이때에 위와 같이 pointer 변수가 이용될 수 있습니다.
여기서 한가지 더 말씀을 드리면,
func1에서 int *a1, int *a2, int *sum 라고 선언하고,
a1[n], a2[n], sum[n]과 같이 배열로 이용을 하였습니다.
이렇게 포인터 변수도 배열과 마찬가지로 [index] 를 붙여서 이용할 수 있습니다.
만약에,
int *x;
라고 선언을 하고,
for(n=0; n<100; n++) x[n] = n;
이라고 coding을 하면,
compiler는 pointer x 가 초기화 되지 않았다고 warning을 띄웁니다.
하지만, warning을 무시하면 어쨌든 compile/link가 되어서 실행 파일이 만들어 집니다.
그리고, 위의 code를 실행 시키면,
운이 좋으면 돌아갈 수도 있고,
운이 없으면 프로그램이 오동작 할 수도 있습니다.
x는 다음과 같이 memory 상에 할당이 되고,
x는 초기화 되어 있지 않기 때문에,
그 안에 있는 값은 unknown 입니다.
--------------------------------------
x | unknown | 0xXXXXXXXX(변수 x의 address)
--------------------------------------
예를 들어서 초기화 되지 않은 x에 0이 들어 있을 경우,
x[n]은 0을 base address로 해서
0,4,8,12...메모리 번지에
0,1,2,3...의 값을
차례로 write 하게 됩니다.
만약에 0 ~ 99x4 번지의 메모리에
현재 프로그램의 수행과 관련이 있는 code나 data가 있다면,
프로그램이 오동작 또는 오류를 띄우며 종료 할 것이고,
그렇지 않다면, 운 좋게 실행 될 수도 있습니다.
만약 위의 예제에서
int *x;
x = a; (또는 x = &a[0];)
라고 x를 초기화한 후에 for-loop이 수행되면,
실질적으로 배열 a에 n 값이 순차적으로 저장되게 됩니다.
또한 func1을 호출할 때에, 100대신에 200을 넣게 되어도 마찬가지 입니다.
아래와 같은 경우는 compile시에 warnging이 보고되지도 않습니다.
func1(&a[0], &b[0], &c[0], 200);
위의 호출은 Build(Compile/Link)는 되지만,
실제 동작은 보장할 수 없고,
대부분 run-time error나 계산의 오류를 보이게 되지만,
아주 아주 운이 좋게도,
a[99], b[99], c[99] 의 메모리 번지 이후에
아무런 변수가 할당이 안되어 있다면,
원하는 대로 돌아가게 됩니다.
Compiler는 초기화 되지 않는 pointer 변수에 대해서 warning을 report 하지만,
위와 같은 경우는 warning 조차 나오지 않고 build가 됩니다.
위의 예제에서 처럼 pointer와 array는 항상 조심해서 다루어야 합니다.
이번 장은 pointer 와 array를 알아봤습니다.
강조하지만,
위의 문법이나 변형된 사용법은 책이나 google 에서 쉽게 찾을 수 있습니다.
쓰다보면 자연스럽게 외워지고,
모르더라도 문법이나 다른 사용법은 쉽게 찾을 수 있습니다.
왜 pointer를 사용해야 하는지.
이렇게 coding을 하면 메모리에서 어떤 access가 일어나는지
큰 그림을 그릴 수 있어야 합니다.
이상으로 이번 장을 마치겠습니다.
Best Regards,
Wolverine
|