Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

메모리

현대적인 컴퓨터는 입력, 연산, 출력의 3가지 과정이 분리되어 있다. 이는 인간의 생각하는 구조와 비슷하다. 여러분의 눈앞에 날아 들면, 눈으로 이를 확인하고 (입력), 뇌로 전달해서 고민을 한다음 (연산), 눈을 감을 건지 얼굴을 돌릴건지를 결정 (출력) 하게 될것이다. 이때 모든 정보는 로 전달되어서, 뇌의 특정부위에 저장된후에 처리됨을 알 수 있다. 컴퓨터 역시 키보드, 하드디스크, CD 등을 통해서 입력된 모든 정보는 일단 메모리에 저장이 된 다음 CPU(:12) 에 의해서 연산처리를 하고, 그 결과를 모니터등을 통해서 출력하게 된다.

메모리의 구조

모든 정보를 일단 메모리에 저장한다는 것은 쉽게 이해할 수 있을 것이다. 그렇다면 메모리를 제어하는 기능역시 필요로 할 것이다. 즉 데이터가 저장될 메모리를 할당해서, 해당 영역에 데이터를 저장하고, 다시 데이터를 꺼내오는 일이다. 이를 위해서는 데이터가 메모리의 어느 지점에 저장되었는지에 대한 정보를 가지고 있어야할 필요가 있다. 이를 위해서 현대의 컴퓨터 시스템은 메모리에 주소를 부여해서 사용하고 있다. 주소는 우편함과 매우 비슷한 점이 있다.

attachment:agl.gif

우편함과 비슷하게 컴퓨터는 메모리에 일련의 숫자로된 주소를 부여해서, 데이터가 저장된 위치를 찾게 된다. 이 주소는 0부터시작하게 된다. 주소의 최소단위는 byte(:12) 다. 이렇게 주소방식을 채택한 이유는 간단한 관리가 가능하며, 관리를 위한 도구를 쉽게 만들 수 있기 때문이다.

포인터

이렇게 메모리의 주소를 관리하기 위해서, C언에서서 사용하는 도구가 바로 포인터다. 포인터는 메모리상의 주소값을 저장하기 위한 데이터 타입으로 4byte 의 저장공간을 가지며, 별표 *를 이용해서 포인터형 변수를 선언할 수가 있다. 변수명앞에 *를 붙여주면 포인터형 변수가 된다.
*data;

여기에서 주목할 점은 포인터는 데이터가 저장된 곳의 주소값만을 가지고 있다는 점이다. 해당 영역의 데이터가 어떤 타입인지를 알수는 없다. 해서 주소가 가리키는 곳에 저장된 데이터의 데이터타입을 명시하기 위해서 *와 함께 데이터 타입을 함께 사용한다. 예를 들어 int 형데이터가 저장된 곳의 주소를 가리키는 포인터형 변수 *data는 다음과 같이 선언된다.
int *data;
이제 우리는 int 형 데이터가 저장된 메모리상의 위치를 알게 되었다. 또한 해당 메모리에 저장될 데이터의 타입이 int 형이란걸 알게 되었으므로, int형 데이터를 마음대로 제어할 수 있게 된다. 우리는 data 를 int 형 포인터 변수라고 한다.

문제
  1. float 형 포인터 변수 fdata 를 선언해 보자.
  2. char형 포인터 변수 cdata 를 선언해 보자.

메모리 할당

변수는 값을 저장하기 위해서 사용한다. int 형 변수 data 에 4를 저장하고자 한다면 아래와 같이 코드를 만들면 될 것이다.
int data;
data = 4;

포인터변수 역시 값을 저장하기 위해서 사용한다. 그러나 이 값이 주소값이라는 다소 특이한 면이 있다. 다른 데이터 타입의 변수들은 대입연산자를 통해서 사용자가 직접 값을 입력할 수 있지만, 주소값은 그렇게 할 수가 없다. 왜냐하면 데이터가 저장될 위치는 단지 운영체제만이 결정할 수 있기 때문이다. 그러므로 포인터에 사용될 주소값을 우리가 직접 결정하는 대신에, 운영체제에게 요청을 하게 된다.

이 요청은 다음과 같은 의미를 가진다.
데이터를 저장하기 위해서 몇 byte 정도의 공간이 필요합니다. 이 공간을 만들고, 이 공간의 주소를 알려주세요.

유닉스 운영체제는 프로그램이 쉽게 이러한 메모리를 요청할 수 있도록 malloc(3) 이라는 함수를 제공하고 있다. malloc(3)함수를 이용해서 필요한 공간만큼을 바이트 단위로 요청하면, 운영체제는 그만큼의 공간을 메모리에서 찾아낸다음, 해당 메모리영역의 주소값을 포인터 데이터 타입으로 알려준다. 다음은 malloc 함수의 사용법이다.
#include <stdlib.h>

void *malloc(size_t size);
size_t 는 typedef unsigned int size_t 로 정의되어 있다. 그러므로 최대 4Gbyte 정도의 메모리 할당이 가능하다. 프로그램이 한번에 사용가능한 메모리의 총크기가 4Gbyte다 라는 말을 들어보았을 것이다. 이 제한은 여기에서 생긴다. 이것은 일반적인 32bit 컴퓨터/운영체제 시스템의 경우이고, 64bit 운영체제라면 테라단위의 메모리 사용이 가능할 것이다. 뭐 굳이 4Gbyte 이상의 메모리를 쓸일이 있을려나 하고 생각할지도 모르겠지만, 과거 DOS 시절 64k 로도 메모리가 차고넘친다고 주장했던 적이 있었음을 기억해보기 바란다. 고성능/대용량 프로그램의 출현으로 64bit 컴퓨팅 환경이 요청이 앞당겨지고 있다.

malloc을 호출했을 때, 리턴되는 값은 메모리의 주소정보를 가지고 있는 포인터이다. 이 포인터는 할당된 메모리의 첫번째 주소를 가리키게 된다.

attachment:malloc.png

그럼 int 형 포인터를 하나 만들고, 여기에 5개의 int 형 값이 들어갈 수 있는 메모리 공간을 만들어 보도록 하겠다.
#include <stdlib.h>

int main()
{
    int *idata;                                   // int 형 포인터 idata 의 선언
    idata = malloc(sizeof(int) * 5);    // 메모리 할당.
}
sizeof는 주어진 데이터타입의 크기를 알려주기 위해서 사용하는 키워드 명령어다. int는 4byte의 크기를 가지므로, 4*5 = 20 해서 5개의 int 형데이터가 들어가는 공간이 만들어지게 된다. malloc 함수는 20byte 만큼 크기의 메모리공간을 확보한다음에, 그 주소를 되돌려준다. 이 주소값은 포인터형 변수 idata에 들어간다.

선언시 정의

위의 경우 malloc 를 이용해서 프로그램의 실행 중간에 메모리 공간을 할당했다. 이렇게 중간 중간 필요할때 할당하는 것을 동적할당(:12)이라고 한다. 그렇지 않고, 포인터 변수가 선언될때 값을 정의 하는 방법도 있다. 아래는 9장에서 다루었던, hello 프로그램의 포인터 버젼이다.
#include <stdio.h>

int main()
{
    char *hello = "hello world\0";
    printf("%s\n",hello);
}
이 경우 프로그램이 실행되는 시점에서 메모리 공간을 할당한 다음, 해당 공간에 "hello world\0"을 저장한 다음, 저장된 주소를 포인터 변수 hello에 넘겨주게 된다. 메모리의 구성은 배열과 동일하다.

문제 hello.c 의 포인트 버젼을 루프와 배열첨자를 통해서 문자를 출력하도록 해보자.

포인터에 접근하기 - 배열첨자 이용

attachment:malloc.png

자 이렇게 해서 메모리 공간이 확보되고, 그에 대한 주소값도 알아왔다. 이제 해당 주소에 접근해서 값을 읽거나 쓰면 될것이다. 메모리가 할당된 위의 그림을 보면 알겠지만, 할당된 크기만큼 인덱스로 접근할 수 있음을 알 수 있을 것이다. 이것은 마치 배열과 같은 접근구조인데, 실제 배열과 동일하게 배열첨자를 이용해서 메모리 공간에 접근할 수 있다. 위의 malloc 프로그램을 약간 수정해서 5개의 공간에 0부터 5까지 채워넣도록 만들어 보자.
#include <stdlib.h>

int main()
{
    int *idata;
    int i;

    idata = malloc(sizeof(int) * 5);

    for (i = 0; i < 5; i++)
    {
        idata[i] = i;
    }
}

포인터 접근하기 - 포인터를 이용

위의 프로그램은 배열첨자를 이용해서 데이터에 접근을 했지만, 포인터 연산을 이용해서 접근할 수도 있다. 포인터도 일반 데이터 타입이므로 +와 같은 연산자를 사용할 수 있다. 만약 포인터에 +1 을 한다면, 포인터가 가리키는 주소값이 1만큼 증가해서 다음 값을 가리키게 될것이다. 포인터 연산은 다음과 같이 간단하게 수행할 수 있다.
char *cdata = "hello world\0";
cdata = cdata+1;
cdata는 "hello world\0" 가 저장된 메모리 영역의 첫번째 주소값을 가지고 있으므로, h를 가리키고 있을 것이다. 여기에 + 1 을 해준다면, 1byte 만큼 증가된 위치인 e를 가리키게 될 것이다. 아래의 그림을 보면 쉽게 이해할 수 있을 것이다.

attachment:pointer2.png

여기에서 혼동할 수 있는 것이 왜 *cdata = *cdata+1; 이 아니고 cdata = cdata+1; 인가 하는 점일 것이다. *는 참조 연산자로 포인터가 가리키는 곳의 을 참조하겠다는 것을 의미한다. 즉 *cdata는 포인터 변수 cdata가 가리키는 주소의 값인 h를 참조하겠다는 의미이며, *cdata+1 을 해버리면, 주소를 증가시키는게 아닌, 값 'h'에 +1을 하는 효과를 지내게 된다. 'h'에 1을 더하면, 'i'가 된다. 이것은 우리가 원하는 바가 아니다.

우리가 원하는 것은 포인터 값이 아닌 주소를 1만큼 증가시키는 것이므로 cdata+1을 해야만 한다. 아래의 코드를 실행시켜 보기 바란다.
#include <stdio.h>

int main()
{
    int *idata;
    int i;
    idata = (int *)malloc(sizeof(int)*5);
    for(i = 0; i < 5; i++)
    {
        idata[i] = i;
    }
    *idata = *idata+2;
    printf("%d\n", idata[0]);
}
*idata 가 가리키는 주소의 값은 0이다. 여기에 2를 더하게 되므로 *idata = *idata+2 는 포인터가 가리키는 첫번째 값을 2로 바꾸어 버리게 될 것이다. idata = idata+2 로 하게 되면 우리가 원하는 바대로, 3번째 값인 2를 읽어오게 될 것이다. 다음과 같이 코드를 변경한 다음 실행해보도록 하자.
    idata = idata+2;
    printf("%d\n", *idata);
이제 idata 포인터는 처음 주소에서 두번째 만큼 증가한 곳을 가리키게 된다. 이제 *idata를 printf로 출력하면, idata가 가리키는 주소의 값인 2를 출력한다. 좀 더 정확히 설명하자면, idata+2를 하게 되면, 현재 주소값에서 4*2 만큼을 더해주게 된다. 왜냐면 idata가 int형 포인터 타입이고, int 데이터 타입은 4byte를 가리키게 되기 때문이다. 만약 char 형포인터변수 였다면 2만큼만 증가하게 될 것이다.

포인터연산에 있어서 덧셈연산은 매우 자주 사용된다. 그래서 증가연산자를 사용할 수 있도록 허용하고 있다. idata = idata+2는 다음과 같이 변경할 수 있다.
*idata += 2;

자 그럼 idata의 모든 값을 출력하는 프로그램을 만들어 보도록 하자.
#include <stdio.h>

int main()
{
    int *idata;
    int i;
    idata = (int *)malloc(sizeof(int)*5);
    for(i = 0; i < 5; i++)
    {
        idata[i] = i;
    }
    for (i = 0; i < 5; i++, *idata++)
    {
        printf("%d\n", *idata);
    }
}

배열과 포인터

배열과 포인터는 기본적으로 동일한 메모리 구조를 가진다. 때문에 포인터를 배열처럼 사용할 수 있는 것과 마찬가지로, 배열을 포인터처럼 사용할 수도 있다. 배열을 사용해서 데이터를 저장했다고 하더라도 결국에는 메모리상의 주소를 이용해서 데이터를 꺼내오는 것이기 때문이다. 이미 앞에서도 포인터와 배열을 함께 쓰는 경우를 봤으므로, 쉽게 이해할 수 있을 것이다.

우리는 데이터의 주소값을 얻어오기 위한 주소 연산자를 이용해서 데이터가 저장된 곳의 주소값을 얻어올 수가 있다. 주소 연산자는 &를 사용하면 된다. 다음은 hello 프로그램의 배열 버전으로 배열변수에서 주소연산자를 이용해서 포인터를 얻어오는 법을 보여주고 있다.
#include <stdio.h>

int main()
{
    char hello[] = "hello world\0";
    char *cdata1;

    cdata1 = &hello[0];
    printf("%s\n",cdata1);
}
위의 프로그램을 약간 수정해서 char *cdata2 라는 포인터 변수를 만들고, w의 주소를 가리키도록 해보자. 아래와 같이 간단하게 구현할 수 있다. 어떤 결과가 출력될지를 예측해 보도록 하자. 아래의 그림을 보면 더욱 쉽게 결과를 예측할 수 있을 것이다. attachment:arrayto.png
#include <stdio.h>

int main()
{
    char hello[] = "hello world\0";
    char *cdata2;

    cdata2 = &hello[6];
    printf("%s\n",cdata2);
}

포인터에서 사용되는 연산들

앞에서 이미 포인터에 사용되는 연산자들과 포인터를 이용해서 할수 있는 연산에 대해서 알아봤다. 이들은 매우 중요하므로 정리하는 차원에서 다시 설명해보도록 하겠다.

참조 연산자

참조 연산자는 *로 해당 주소가 가리키는 곳의 값을 참조하며 다음과 같이 사용할 수 있다.
#include <stdio.h>

int main()
{
    char *data = "hello world\0";
    int i;
    char *cp = data;

    for (i = 0;;i++)
    {
        if (*data == '\0') break;   // data가 가리키는 주소가 참조하는 값이 '\0'이면 루프를 빠져나온다.
        printf("%c\n", *data);      // 참조 연산자를 이용 data가 가리키는 주소의 참조값을 출력한다.
        *data++;                         // 증가연산자를 이용해서 포인터를 1만큼 증가시킨다.
    }

    printf("============\n");
    data = cp;
    for (i = 0;;i++)
    {
        if (*data == '\0') break;  // 주소의 참조값이 '\0'이면 루프를 빠져나온다.
        printf("%s\n", data);       // 포인터가 가리키는 곳의 데이터를 출력한다. 
                                                // %s는 '\0'을 만나기 전까지의 데이터를 화면에 출력한다.
        *data++;                 
    }
}
위 프로그램을 실행시키면 다음과 같은 결과를 출력한다.
h
e
l
l
o

w
o
r
l
d
============
hello world
ello world
llo world
lo world
o world
 world
world
orld
rld
ld
d
왜 이런 결과가 나오는지 이해하는건 그리 어렵지 않으리라 생각된다.

주소 연산자

어떤 값이 저장된 곳의 주소를 얻어오기 위해서 사용하는 연산자이다. 다음과 같이 사용할 수 있다.
    char *hello = "hello world";
    char *data;
    data = &hello[0];
    printf("%s\n", data);  // hello world 가 출력된다.
    data = &hello[6];
    printf("%s\n", data);  // world가 출력된다.
포인터 변수에 들어가는 것은 값이 저장된 데이터의 주소이므로, 주소연산자 &를 이용해서 얻어온 주소를 포인터 변수에 대입할 수 있게 된다.

덧셈연산

포인터는 덧셈연산이 가능하다. 왜냐하면 저장된 데이터가 주소이기 때문이다. 주소에 숫자를 더하는 방식으로 메모리의 다음위치에 있는 주소를 얻어올 수 있기 때문이다. 이러한 덧셈연산이 가능한건, 데이터를 저장할 메모리를 할당할때 연속된 공간에 할당하기 때문이다. 이를테면 주소+1 하면 다음 주소를 가리키게 된다.
    char *hello = "hello world";
    printf("%s\n", hello+1);

마찬가지로 포인터 변수끼리 더하거나 빼는 것도 가능하다.
    char *hello = "hello world\0";
    char *fp = hello;
    char *sp = hello+6;

    printf("%d\n", sp-fp);
메모리 상에서 sp와 fp의 거리는 6일 것이다. 그러므로 두개의 포인터를 뺀 결과로 6이 출력될 것이다. 이런 포인터 연산을 어디에 써먹을 수 있을지 감이 잡히지 않을 수 있을 것이다. 그래서 포인터 연산을 이용한 간단한 예제프로그램을 만들어 보았다. 아래의 예제 프로그램은 "::" 사이에 있는 문자만을 얻어오는 프로그램이다.
#include <stdio.h>

int main()
{
    char *hello = "aaa::data::bbb\0";
    char *org = hello;
    char *fp;
    char *sp;

    char buf[10] = {'\0',};

    fp = strstr(hello, "::");      
    printf("%s\n",fp);          // fp는 ::data::bbb를 가리킨다.

    sp = strstr(fp+2, "::");  // fp+2 는 data::bbb 이다.
    printf("%s\n",sp);         // 그러므로 sp는 ::bbb가 된다.

    memcpy(buf,fp+2,(sp - fp)-2); // fp+2는 data::bbb 이다.
                                                    // sp - fp 는 6이다. 여기에는 "::"도 포함되어 있으므로 -2를 해준다.
                                                    // 결국 data::bbb 에서 4만큼으 크기의 데이터를 buf에 복사한다.

    printf("%s\n",buf);                  // 그러므로 data가 출력된다.
}
strstr(3) 은 주어진 문자열을 찾는 함수로, 문자열이 발견된 위치의 포인터를 리턴한다. memcpy(3)는 데이터를 정해진 크기만큼 복사하기 위해서 사용하는 함수다.

다차원 포인터

배열과 마찬가지로 다차원 포인터를 사용한다. 말이 다차원이고, 3차원 이상을 넘어가면 인간이 인지하기가 매우 어렵기 때문에 - 우리는 3차원 공간에 살고 있지만 3차원을 머리로 그려내는건 쉬운일이 아니다. 3차원 전략 시뮬레이션이 컨트롤하기 까다로운 이유가 여기에 있다. - 일반적으로 다차원 포인터란 2차원 포인터만을 가리킨다.

포인터는 데이터가 저장된 주소를 가리키는 데이터 타입이다. 이는 어떠한 종류의 데이터타입이라도 가리키는 포인터의 생성이 가능함을 얘기한다. int 형 포인터, float 형 포인터가 좋은 예가 되겠다. 2차원 포인터는 포인터 데이터 타입을 를 가리키는 포인터이다. 이러한 특징 때문에 포인터의 포인터라고 부르기도 한다. 2차원 포인터의 가장 단적인 얘는 라인단위 편집기를 예로 들 수 있을 것이다. 아래와 같은 문장이 있다고 가정해 보자.
Hello World!!
My name is yundream.
What's your name.
Thank you.

이 문장을 각각의 라인단위로 저장을 하는 프로그램을 만든다고 가정해 보자. 그렇다면 4개의 char 형 포인터가 필요할 것이다. 이제 이 4개의 char 형 포인터 데이터를 저장하기 위한 포인터변수를 만들면 될것이다. 포인터를 저장하는 포인터변수인 셈이다. 포인터의 포인터변수는 별표를 하나 더 붙이는 것으로 간단히 선언할 수 있다. 위 예의 경우 char 포인터를 저장하기 위한 포인터는 아래와 같이 선언할 수 있다.
char **data;
차원이 늘어날 수록 그만큼의 별표를 붙여주면 된다. 사용할일이 거의 없겠지만, 3차원 포인터는 다음과 같다.
char ***data;

그럼 2차원 포인터를 이용해서 위의 문장을 출력하는 줄단위 출력 프로그램을 만들어 보자. 포인터형 변수의 이름은 char **data로 선언한다. 선언을 했다면, 메모리 할당을 해야 할것이다. 2차원 포인터 이므로 2번의 메모리 할당이 이루어져야 한다.
  1. 4개의 문자형 포인터를 저장하기 위한 공간 할당.
  2. 각 문자데이터를 저장하기 위한 공간할당.
우선 문자형 포인터를 저장하기 위한 공간은 다음과 같이 할당할 수 있다.
char **data;
data = malloc(sizeof(char *)*4);
데이터를 저장하기 위한 공간을 할당하려면, 데이터 타입의 크기를 알고 있어야 한다. 앞서 sizeof를 이용하면 데이터 타입의 크기를 얻어올 수 있음을 언급했다. 우리가 저장하기 위한 데이터 타입은 문자형 포인터이므로 sizof(char *)를 이용해서 크기를 얻어올수 있다. 이게 4개가 필요하므로 *4를 해주면 4개의 문자형 포인터 데이터를 저장하기 위한 공간을 할당받게 된다. 포인터 데이터 타입의 크기는 4 이므로 16byte 만큼의 공간을 할당받게 된다.

이제 4개의 문자형 포인터가 사용할 공간을 할당하고, 그 주소값(포인터)를 얻어와야 할 차례다. 원칙적으로 하자면, 각 문자열의 크기만큼 다르게 할당해야 겠으나, 그렇게 하면 귀찮으니, 모든 문자열을 넣기에 충분하다고 생각되는 25byte 를 동일하게 할당해보도록 하겠다. for 루프문을 이용하면 간단하게 할당할 수 있다.
char **data
data = malloc(sizeof(char *)*4);
for (i = 0; i < 4; i++)
{
    *data = malloc(sizeof(char) * 25);
    *data++;
}

attachment:pointer_array.png

이렇게 해서 25 byte 만큼의 공간이 할당되었다. 만약 3차원 포인터라면 3번의 메모리 할당이 이루어져야 할것이다. 그런데 솔직히 2번의 메모리 할당이 이루어지는 2차원 포인터도 이해하기가 쉽지 않을 건데, 3차원 포인터를 이해하기란 정말 힘들 것이다. 프로그램을 만들때는 특수한 경우가 아니면 가능한 "이해하기 쉬운 코드"를 작성하는 방향으로 가야 한다는 점에서, 3차원 이상의 포인터는 고려하지 않는게 좋을 것이다. 실제로 3차원 이상의 포인터를 볼 수 있는 경우는 거의 없을 것이다. 필자 역시 3차원 포인터를 사용한 프로그램을 본 기억은 거의 없다.

메모리 상에서의 실질적인 데이터공간 할당구조는 아래 그림과 같을 것이다.

attachment:point_array2.png

다음은 완성된 프로그램이다. 프로그램의 이름은 point2.c로 하겠다.
#include <unistd.h>

int main()
{
  char **data;
  char **org;
  int i;

  data = (void *)malloc(sizeof(char *)*4);  // 4개의 포인터를 저장하기 위한 메모리 공간 확보
  org = data;                                              // 원래 포인터의 주소를 저장하기 위한 용도.
  for (i = 0; i < 4; i++)
  {
    *data = malloc(sizeof(char)*25);  // 각각의 포인터에 최대 25개의 char 데이터를 저장하기 위한
    *data++;                                       // 공간을 확보한다.
  }

  data = org;
  strcpy(*data,"hello world!!\0");

  *data++;
  strcpy(*data,"My name is yundream.\0");

  *data++;
  strcpy(*data,"What's your name.\0");

  *data++;
  strcpy(*data,"Thank you.\0");

  data = org;
  for (i = 0; i < 4; i++)
  {
    printf("%s\n", *data);
    *data++;
  }
}

할당되지 않은 포인터

그렇다면 할당되지 않은 포인터를 사용하면 어떻게 될가. 이전장에서 값이 할당되지 않은 변수는 알수없는 값이 들어있다는 것을 배웠었다. 메모리공간을 요청받은 운영체제는 메모리 공간만 할당하지, 그 메모리 공간을 0 등으로 초기화 시켜주는 일은 하지 않기 때문이다.

포인터변수도 마찬가지로, 어떤 값을 가지고 있을지 알수 없다. 다른 데이터 타입에 비해서 더욱 치명적인것은 주소값을 가지고 있다는 점이다. 알수 없는 주소값이 들어있다는 뜻인데, 이 주소값이 다른 프로세스가 사용중인 주소값일 수 있기 때문이다. 여기에 데이터를 저장하려고 시도할경우 프로그램이 죽어버리게 된다. 그러므로 포인터는 사용하기전에 메모리 공간을 할당해야 한다. 아래의 프로그램을 컴파일하고 실행시키면 비정상 종료되어 버릴 것이다.
int main()
{
    char *data;
    strcpy(data, "hello world\0");
}
반드시 malloc() 함수등을 이용해서, 메모리를 할당받아야 된다.

선언시 포인터의 초기화

int a; 라는 값을 선언한다고 가정해 보자. 그냥 선언만 할 수도 있겠지만, 어떤 값이 들어가 있는지 알 수 없고, 때문에 잘못된 프로그램을 만들 수도 있다. 예를들자면 다음과 같은 경우다.
int a;
... 
...
if (a ==0)
{
   // 이런저런 코드들
   ...
}
a에는 알 수 없는 값이 들어가 있으므로 잘못된 결과를 출력할 것이다. 이런 실수는 그다지 많이 발생하진 않겠지만, 미연에 예방하는 차원에서 일반적으로 사용하는 값으로 초기화 시켜줄 필요가 있다. int 형이라면 0 이 될것이다.
int a =0;
float b = 0.0F;

포인터의 경우에는 NULL이라는 것을 사용한다. 이것은 할당되지 않았음을 의미하는 키워드로 다음과 같이 사용될 수 있다.
char *data = NULL;
이렇게 NULL 로 초기화 할경우, 포인터를 사용할때 다음과 같이 포인터값을 검사함으로써, 좀더 robost(견고한) 프로그램을 만들 수 있게 된다.
char *data = NULL;
if (data == NULL)
{
    data = malloc(24);
}
strcpy(data, "hello world\0");

문제

  1. 배열과 포인터는 메모리 구조의 유사성으로 거의 동일하게 사용할 수 있음을 배웠다. 그렇다면 point2.c 프로그램을 배열을 사용하도록 바꾸어 보도록 하자.