자료출처 : 권남수님의 Game Programming을 위한 C 강의

포인터응용 2

초보의 글이므로 잘못되거나 서투른 표현이 있을 것입니다. 그러나 읽는 분을 위해 최선을....


결론 : 추후 보완!!


1. 동적 메모리 할당 (사용된 소스)
무엇을 하던간에 아무튼 메모리를 이용하게 되며, 변수나 배열을 이용하면 기억 공간을 많이 잡아 먹기에 메모리의 낭비가 된다. 따라서 메모리를 효율적으로 관리하기 위해 malloc함수를 이용하게 된다. 이함수를 이용하면 꼭 필요한 기억 공간만을 미리 내가 할당시켜 두고 사용했다가 다시 free 함수로 사용한 메모리를 돌려 주는 합리적인 방법이다.

아래 예문에서는 I love C라는 - 그림 크기 등도 물론 되겠지만 - 문자열을 malloc 함수를 통해 메모리 공간을 사용하고 다시 메모리를 돌려주는 공부를 한다. 이때 사용되는 함수로는 문자열의 길이를 구하는 strlen 함수, 문자열을 복사해 주는 strc 함수가 사용될 것이다.

동적메모리 할당
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void strc(char *array, char *name, int len); // main 함수를 앞쪽에 위치시켰기에 함수 선언을 이렇게 해 주어야 한다.

void main()
{
char array[] = "I love C"; // 문자열을 배열명 array에 넣어 두었다.
int len; // 문자열 길이를 위한 변수
char *name; // malloc로 메모리 할당한 것을 저장할 공간.

len = strlen(array);
name = (char *)malloc(sizeof(char)*(len+1));
strc(array, name, len); // (거꾸로의 방향으로) array의 내용을 name으로 복사한다.

printf("array : %s\n", array); // 배열을 통해 화면에 출력함.
printf("name : %s\n", name);
// 포인터를 통해 화면에 출력함.
free(name); // 사용한 동적 메모리를 다시 돌려 준다.
}

void strc(char *array, char *name, int len) // array의 주소를 name 주소로 복사한다.
{
int i;

for(i=0; i <len + 1; i++) // 문자열 길이 만큼
{
*(name + i) = arrary[i]; // 배열 내용을 포인터로 전달한다.
}
}

정리
필요한 내용만을 정리해 보자. (malloc 소스 다운)

① char형의 데이터형을 가진 arrary라는 배열명을 선언했고, 이 안에 I love C를 넣어둔다.
② 문자열의 길이를 구하는 함수 strlen을 통해 변수 len에 문자열 길이가 들어간다.
name = (char *)malloc(sizeof(char)*(len+1));
포인터 name에다가 malloc로 구한 메모리를 할당시킨다. 즉, 내가 원하는 만큼의 메모리 공간을 확보시켜 둔다. 어떻게 계산할까?

우선 malloc 함수는 리턴값이 void형이므로 형을 맞추기 위해 캐스트 연산자를 통해 즉, - char * - char형으로 형변환을 시켜야 한다. 또한, 문자열의 크기도 당연히 메모리에 포함시켜 주어야 하므로 크기를 구하는 연산자를 통해 char형의 크기 - 즉, 1바이트 -에다가 문자열의 길이인 len 만큼을 곱하기하여 메모리를 확보시킨다. 또한, 문자열은 끝에 NULL 문자를 넣어주어야 하므로 +1의 길이가 추가된다. 이렇게하여 가장 합리적인 메모리만이 확보되었다.

strc라는 문자열을 복사해 주는 함수를 사용하는데 - 일반적인 순서와 달리 - array의 내용을 name으로 len 길이 만큼을 복사시킨다.

for(i=0; i <len + 1; i++) // 문자열 길이 만큼
for 문에서는 문자열 길이 만큼을 순환시킨다. 단, 본 소스는 문자열을 다루기에 NULL 문자를 계산해서 +1을 해 두어야 한다.

⑥ malloc로 할당시켜 두었던 name 포인터를 메모리에서 해제시킨다.

그 밖에 문자열을 합치는 예문 - 이것도 malloc를 사용 - 은 생략한다.


2. 함수에서의 포인터 활용
아래의 예를 통해 변수 a와 b에 들어가 있는 값을 서로 바꾸려고 한다. 이때 포인터를 사용하지 않는다면 a, b의 값이 바뀌지 않지만 포인터를 사용하면 - return을 사용하지 않더라도 - 그 값이 서로 바뀌는 신기함을 볼 수 있다.

포인터를 통한 swap (사용된 소스)
#include <stdio.h>

void swap(int *c, int *d);

void main()
{
int a, b;
a = 111;
b = 222;

swap (&a, &b);
printf("a = %d,b = %d", a, b);
}

void swap (int *c, int *d) // a와 b의 주소가 넘어간다.
{

int imsi; // 이렇게 새로운 변수를 만들어서 swap 과정을 실시한다.

imsi = *c;
*c = *d;
*d = imsi; // return 조차도 없지만..
}

위처럼 a에는 111을 b에는 222를 넣었지만 swap 함수의 포인터를 통한 인자 전달과 그 내부에서 imsi변수를 통해 값을 교환하므로써 결국 a는 222로, b는 111로 값이 바뀌었다. return값도 없는데...
(swap 소스 다운)

3. 2차원 배열의 주소
다차원 배열이란 2차원 이상의 배열로 구성된 배열을 말하는데 행(= 세로) 과 열(=가로)로 이루어진다. 2차원 배열은 1차원 배열이 다시 배열의 인자로 1차원 배열을 갖는 배열이다. 이 뜻은 - 메모리라는 것이 원래 순차적으로 정렬되어 있는 것이므로 - 이러한 순차적으로 되어있는 메모리를 마치 다차원 배열인 것처럼 관리하고 있다는 말이된다. 예를 들어

int a[4][3];

이처럼 int형의 a라는 배열명을 가지는 2차원 배열을 선언했다. 행 즉, 세로로 4줄, 열 즉, 가로로 3줄이다. 따라서 배열의 요소는 12개가 되는데 위 배열의 "구조"를 따져보면 다음과 같다.

행과 열
0
1
2
0
a[0][0]a[0][1]a[0][2]
1
a[1][0]a[1][1]a[1][2]
2
a[2][0]a[2][1]a[2][2]
3
a[3][0]a[3][1]a[3][2]

배열의 앞부터 살펴보면 순서는 다음과 같다. a[0][0] a[0][1] a[0][2] a[1][0] a[1][1].....a[3][2] 자! 그렇다면 배열이 메모리에서 실제로 어떻게 구성되는지를 알아보자. 결론은 "행"을 중심으로 하여 "부분배열"이란 개념으로 우리는 이해해야 할 것이다. 즉,

부분 배열 a[0]a[0][0]a[0][1]a[0][2]
부분 배열 a[1]a[1][0]a[1][1]a[1][2]
부분 배열 a[2]a[2][0]a[2][1]a[2][2]
부분 배열 a[3]a[3][0]a[3][1]a[3][2]

앞에서는 1차원 배열에 대한 것을 공부했다면, 2차원 배열에서의 a 배열명의 주소를 알아보자. 만약 a와 a[0] 그리고 &a[0][0]의 주소는 어떻게 될 것인가? 결론은 이들 3가지의 주소가 모두 같다. 이유는 모두 선두 번지를 뜻하기 때문이다.

4. 포인터 배열
배열은 포인터와 밀접한 관계를 가진다고 했다. 그럼 이번에는 문자열을 2차원 배열로 표현해 보자.

char a[3][10] = {"I", "love", "C-Language"};

이렇게 char형 2차원 배열을 문자열로 초기화 시키면 실제 메모리에서는 - 각 문자열에 대해 가장 긴 것인 10을 기준으로 메모리를 잡기에 즉, - 3*10 = 30바이트 크기를 메모리를 잡게 되어 아래처럼 글자가 적히지 않는 곳에 대한 메모리가 낭비된다. (실제 소스에서는 - 문자열이기에 - a[3][11]로 메모리가 설정된다.)

I
l
o
v
e
C
-
L
a
n
g
u
a
g
e

그러나 이러한 표현을 다음과 같이 포인터 배열로 잡으면 이러한 메모리 낭비를 막게된다. 즉,

char *a[3] = {"I", "love", "C-Language"};

포인터배열의 경우는 *a[3] 처럼 선언하게 되는데 이 뜻은 *a를 인자로 가지는 배열을 3개 선언한다는 뜻이 된다. 즉, *a와 *(a+1) 그리고 *(a+2)가 되며 각각의 문자열이 3개의 포인터 배열을 뜻하게 된다.

포인터배열을 사용하게 될 때는 위처럼 문자열을 넣어주는 초기화를 시켜 주어야 한다. 이렇게 초기화를 시키면 자동배열이 되며, 이 3개의 포인터배열은 각각의 문자열의 선두번지만을 가지게 되어 각 문자열에 맞게 메모리가 할당된다. (또한, - 문자열을 복사하는 경우처럼 - 포인터가 가리키는 주소가 없을 경우에는 메모리를 malloc 함수로 할당시켜 포인터에다가 넘겨 주었지만 여기서는 선언과 함께 초기화를 시켜 주었기 때문에 - 즉, 3개의 각각의 포인터는 각 문자열의 첫번째 주소를 가리키기 때문에 - 따로 메모리 할당이 불필요하게 된다.)

이하는 다소 복잡하며, 또 필요성을 크게 느끼기 못해서 아직 정리를 못했습니다.

5. 2차원 배열과 포인터의 관계



6. 3차원 배열


7. 다중 포인터


8. void형 포인터


(끝)

2005/08/19 04:16 2005/08/19 04:16
Trackback address :: http://4ellene.net/tt/trackback/321

Comments List

  1. brampton nude resort 2008/05/23 07:31

Write a comment.

[로그인][오픈아이디란?]