관리 메뉴

IT 컴퓨터공학 자료실

초급편 5. 포인터의 형태 본문

컴퓨터공학/포인터

초급편 5. 포인터의 형태

윤맨1 2015. 6. 30. 11:08

                                          포인터 토대가 된 책

                                               포인터의 형태

여기서 변수는 정수형 int, 문자형 char 부동 소수점 형 double 등의 형태가 있다는 점에 유의하여 포인터를 살펴 보자.

이러한 변수의 형식은 그 변수의 값이 필요로하는 메모리의 정보를 가지고있다. 일반적인 UNIX에서는 int = 4byte, char = 1byte, double = 8byte가 보통이며, 변수의 형태에 따라 변수가 메모리에서 차지하는 것이다 크기가 확보되어있다. 크기를 알기 위해서는

printf ( "size of int = % d \ n", sizeof (int)); 
printf ( "size of char = % d \ n", sizeof (char)); 
printf ( "size of double = % d \ n", sizeof (double)); 
printf ( "size of pointer to char = % d \ n", sizeof (char *)); 
printf ( "size of pointer to double = % d \ n", sizeof (double *)); 
(프로그램 5-1)

같은 요령으로 표시 해 보면 좋다.

포인터 자체는 주소를 나타내는 숫자에 불과하기 때문에, 보통 int와 같은 크기이지만, 그 포인터가 가리키는 대상의 크기는 천차만별이다. 이를 위해 포인터에도 형태가 존재한다. 그래서 포인터를 선언 할 때 그 포인터가 무엇을 나타내는 것인지를 명시하여 선언 할 필요가있다.

사실 어셈블리 수준에서는 단순한 주소를 나타내는 숫자이기 때문에 포인터의 형태는 존재하지 않는다. 그러나 포인터의 형태를 구별하고 대입 관계를 확인하여 포인터의 실수를 미연에 방지 할 수 있기 때문에 C에서 포인터의 형태를 적극적으로 확인하는 것이다. 또한 포인터가 가리키는 데이터 객체의 크기가 천차만별 인 것은 포인터의 가산 대해 사실 특별한 취급을 필요로하게된다.

위의 예제에서는 for 루프에서 "p ++;"식으로 포인터를 가산하고있다. 포인터 변수 p가 가리키는 데이터 오브젝트는 int 형이며, 그 크기는 4byte이다. 이를 위해 배열 x 1000 번지부터 시작하면 & x [0] = 1000 & x [1] = 1004 & x [2] = 1008 & x [3] = 1012라는 식으로 4 개 간격으로 주소를 검색 되게된다. 그래서 포인터 변수 p에 1000 번지의 주소가 들어 있다고하면 포인터 변수의 가산하여 다음 데이터 객체를 가리키는 것이라면, p + 1 = 1004이 될 것이다. 즉, 수치의 덧셈과 달리, 포인터의 덧셈은 다음과 같이 계산된다.

p + sizeof (포인터가 나타내는 형태) * 증가 증가

다시 확인하지만, 포인터 자체는 단순한 int 값을 같은 크기 밖에 없다. 그래서 포인터는 int 값도 캐스팅 할 수 있으며, 다른 형태의 포인터로 캐스팅 할 수도있다. 다음 예는 까다로운 예이며, 결코 흉내해야 할 프로그램은 아니지만,이 사정을 잘 설명 할 수있다.

char s [4] = {1, 2, 3, 4};     
    / * 예를 들어 1000 번지 (1)에서 1003 번지 (4)까지 * /
int * p;

void main ()
{
        p = (int *) s;       / * p = 1000 * / 
        / * 1000 번지에서 int의 크기 분 (4byte)이 0 * /
        * p = 0;     
        printf ( "% d % d % d % d \ n", s [0], s [1], s [2], s [3]);
}
(프로그램 5-2)

원래 배열 이름 s는 char 형의 포인터이다. 그 수치를 억지로 int 형 포인터로 캐스팅 해주고, int 형 포인터 p에 대입한다. 그러면 포인터 p에 0을 대입하면, s [0]이 0이 될뿐만 아니라, 배열 s의 4 가지 요소가 0이된다. 이것은 포인터의 형태를 억지로 바꾸어 대입했기 때문에 이런 효과를 얻을 수있는 것이다.

이처럼 포인터의 형태는 억지로 캐스팅 다른 형태로 본다 수도 있지만, 그렇지 아니라, 특히 대상을 명시하지 않는 포인터로 정의 할 수있다. 이러한 지시 대상을 명시하지 포인터는 void * (보이드 포인터)로 불린다. void *는 실제로 값을 참조 할 때마다 캐스팅해야한다. 왜냐하면 가리키는 대상의 데이터 객체의 크기도 정확히 알 수 있기 때문이다. 그래서 void *는 p = p + 1은 크기를 알 수 없기 때문에 계산을 할 수 없다. 계산을하기 위해서는,

        p = (char *) p + 1;       / * p = p + 1 * / 
        p = (int *) p + 1;        / * p = p + 4 * /
(프로그램 5-3)

같은 요령으로 캐스팅을하지 않으면 안된다.

void *의 예를 표시한다.

int x = 10;
char * s =` "test";
double y = 3.1415;
void * p;

void display (void * p, int type)
{
        switch (type) {
        case 0 :   / * int 형의 경우 * /
                printf ( "int : % d \ n", * (int *) p);
                break;
        case 1 :   / * 문자열을 전달하는 경우 * /
                printf ( "string : % s \ n", (char *) p);
                break;
        case 2 :   / * double 형의 경우 * /
                printf ( "double : % lf \ n", * (double *) p);
                break;
        }
}

void main ()
{
        p = (void *) & x;
        display (p, 0);
        p = (void *) s;
        display (p, 1);
        p = (void *) & y;
        display (p, 2);
}
(프로그램 5-4)

이와 같은 예는 다소 까다로운이지만, 구조체 포인터가 관련되면이 같은 취급을하면 편리한 경우가있다.

int의 값과 포인터는 실질적으로 동일한 것이다. 것은 C 프로그램에서 직접 수치를 포인터로 캐스팅하고 사용할 수 있을까라는 문제를 생각해 보자. C 프로그램에서도 int의 값을 직접 포인터를 캐스팅하는 것은 가능하다.

void main ()
{
        char * p;
        p = (char *) 0;
        * p = 10;
}

이것은 매우 간단하게 세그멘테이션 폴트 (메모리 보호 위반)를 일으킬 수있는 프로그램이다. 즉, 컴파일은 가능하며, 합법적 인 C 언어이다. 그러나 할당 된 주소 0을 사용하면 세그멘테이션 폴트가 일어난다는 UNIX의 상식에 따라 CPU에 의해 중지시킬 수있는 것이다.

것은 그렇지 않다 주소를 포인터로 할당하고 어떤 기능을 실현하는 것은있는 것일까. 실제로 커널 코드와 디바이스 드라이버는 실제 메모리에서 특정 주소가 주변 장치에 입출력 포트가있는 경우가 있고, 그들에 대한 액세스는 위와 같이 즉치 캐스팅 를 포인터에 대입하여 액세스한다. 하지만 일반 응용 프로그램에서는 대부분 나타나는 것은 아닐 것이다. (root 권한을 가지고 있고, SVGAlib 프로그래밍을 할 때 정도 일까?)

과제

  1. 억지로 포인터를 캐스팅하면 정보를 떨어 뜨리거나 할 수있다. 다음 예제에서 어떤 출력을 얻을 수 있을지 생각에서 확인하라. 결과는 Intel 등의 리틀 엔디안 CPU와 PowerPC 등의 빅 엔디안 CPU와 다르다.
    int value = 256; / * 적당히 값을 바꾸어 본다 * /
    void main ()
    {
            char * p;
            p = (char *) & value;
            printf ( "char value = % d \ n", * p);
    }
    
  2. 무리한 캐스트를 사용하면 비트 패턴이 int와 float (모두 4byte)에서 같은 수치를 찾아 프로그램을 작성할 수있다. 그것을 쓰고 어떤 수치가 그렇지인지 찾아보고 있어요. 프로그램 팁
            unsigned int i;
             / * unsigned int의 최대 값 * /
            for (i = 0u; i <4294967295u; i ++) { 
                    ...................
            }
    
    같은 요령으로 루프를 만들면 좋다. 약간 실행 시간이 걸리지 만 찾을 수있는 것이다.