。공부 。2008.01.27 20:57
혹시 함수도 주소값을 갖고 있다는거 아시나요?
그럼 함수의 자료형만 안다면 함수역시 포인터가 되는거 아닐까요?
그렇습니다 함수역시 주소값을 갖고 있고 자료형또한 갖고 있습니다.
그럼 주소는 어떻게 되고 자료형은 무엇을 갖는지 그 자료형을 어떻게 알아 내는지 를 알아보죠
간단히 함수의 포인터 타입을 알아보자는겁니다.

void show(int a){
    printf("%d \n",a);
}
위와같은 함수가 있습니다 간단히 int형 변수를 매개변수로 받아 출력을 하는 show합수지요
함수의 자료형을 얻기 위해서 우린 함수의 리턴타입과 매개변수타입이 무엇인지를 알아야 합니다.
위의 show 함수의 리턴타입은 void형 이고 매개변수는 int형이되겠지요..
그럼 show의 포인터 를 만들이렇게 됩니다.
void(*show_ptr)(int) = show;
위의 문장을 보면 잘 이해가 안가실겁니다. 위의 포인터타입을 영어해석(?) 하듯 읽어 보지요.
show 함수의 포인터 show_ptr을 선언하는데 리턴타입은void형이고 매개변수는 int형인 포인터 *show_ptr을 선언.

음... 어렵나요? 그럼 이렇게 비교해보죠
void show(int a)         --> show함수        
void(*show_ptr)(int)    --> 포인터 show_ptr
어떻습니까 둘이 많이 닮아있죠? 다 똑같은데 함수에 괄호()와 이름앞에 '*'붙은거 뺴곤 똑같다는것을 알수있습니다.

다른 예를 한번더 들어보죠.
int fct(char *str, int aa)       
void(*show_ftc2)(char*, int)
이제 좀 감이 오시죠? 이렇듯 함수를 포인터로 선언한것을 우리는 함수포인터라 합니다.
하지만 둘이 완전히 같은것은 아닙니다 ..

[ 함수와 함수포인터의 차이 ]
        함수           --> 포인터 상수
        함수 포인터 --> 포인터 변수

[ 함수포인터 간단한 예제]
#include <stdio.h>

void show(int a){
    printf("%d \n",a);
}

int main(void){
    int aa = 10;
    void(*show_ptr)(int) = show;
   
    printf("%d %d \n",show,show_ptr);

    show(aa);
    show_ptr(aa);

    return 0;
}

함수의 정체를 알았기 때문에 우린 이제 show() 함수를 이렇게 이렇게 읽어야 될겁니다. show라는 포인터가 가르키를 함수를 호출하면서 인자값으로 상수 10을 전달하라고 말이죠.


신고
Posted by kyoe
。공부 。2008.01.23 00:22
1차원 배열의 포인터 타입을 결정할때 우리는 포인터의 자료형과 포인터 연산시 증가하는 바이트 크기가 같다 는것을 알고 있다.
그럼 2차원 배열은 어떨까? 1차원 배열과 같을까? 이렇게 같으면 얼마나 좋으련만.. 그렇지가 않다.
포인터의 자료형은 1차원배열과 마찬가지로 포인터가 가르키는 자료형과 일치하지만 포인터 연산시 증가하는 바이트의 크기가 틀리다. 같은 int형 배열이더라도 증가하는 값은 4byte로 동일한것이아니라 그때 그때 틀리다는 것이다.

예를 들어 보자.
int arr[3][2] = {1,2,3,4,5,6};
위와같이 3*2배열이 있다.
자료형은 당연히 int형일테지만 포인터 연산시 증가하는 값은 몇byte일까?
확인하는 간단한 방법은 주소값을 출력하면 되겠죠
printf("arr[0] : %d\n",arr[0]);
printf("arr[1] : %d\n",arr[1]);
printf("arr[2] : %d\n",arr[2]);
2차원 배열에서 arr[0] 이라 함은 배열의특정 위치를 가르키는 포인터가 된다 즉 주소값을 갖게된다.
하지만 arr[0][0] 은 배열의 요소값을 가르키기때문에 출력하면 1값이 출력될것이다.
이렇게 주소를 출력하면 이런값이 뜬다 결과값이 개개인 컴퓨터마다 틀리겠지만 증가값은 같다.
arr[0] : 0x00
arr[1] : 0x08
arr[2] : 0x16

분명 int형 배열인데 왜 4byte씩 증가되지 않고 8byte씩 증가 되었을까?
표현이 잘될런지는 모르지만 그림으로 설명하면 아래와 같이 설명할수있다.

arr[0] -------->   1  ,             2       
                         a[0][0]      a[0][1]
                         0x00           0x04   

arr[1] -------->   3  ,             4      
                        a[1][0]      a[1][1]
                         0x08           0x12  

arr[2] -------->   5  ,             6      
                        a[2][0]      a[2][1]
                         0x16           0x20  

arr[0]이 배열의 첫번째 행을 가르키는 것을 알수 있다 3*2배열이기 때문에 한 행에는 2개의 값이 있고 그행의 첫번째 주소를 가르키게 되는것이다.
arr[1]은 첫번째행의 두번째 요소를 가르키는것이아니라 두번째 행인 a[1][0]을 가르키는 것을 알수있수있다 그럼 위의 결과값이 왜 4byte씩 증가 되지 않고 8byte씩 증가 되었는지 우리는 그 결론은 여기서 찾을수 있다 2차원 배열의 포인터연산시 증가하는 값은 포인터의 자료형의메모리크기*행의 갯수!
행단위증가를 하는것을 알수있다.

[ 다차원 포인터타입 정의 ]
     - 가리키는 대상의 자료형
     - 포인터 연산시 증가하는 바이트의 크기

위에서 정의 내리기를 다차원 배열의 포인터 타입은 가리키는 대상과 포인터연산시 증가하는 바이트의 크기 가 같으면 같은포인터타입이라는것을 알수있다.

int array1[3][2];
int array2[2][3];
위와같이 두개배열의 포인터타입은 서로 일치할까요?
그렇죠.. 아닙니다 서로 가르키는 대상이 int형인건 같지만 포인터 연산시 증가하는 바이트의 크기가 틀리죠.. array1은 8byte , array2는 12byte씩 증가하게 됩니다.

이렇게해야 서로의 포인터타입이 같게 되는것이죠
int array1[3][2]
int array2[4][2]

그럼 위의 이차원 배열을 포인터에 대입해 보겠습니다.
int (*pp)[2] = array1;
어때요? 이해가 가나요? 어떻게 보면 복잡해보이고 헷갈릴수 있습니다.
우리는 int* pp[2]; 이런문장을 배웠기 때문일텐데요 이문장과 위의 문장은 완전히 다른 코드입니다.
int* pp[2] 를 해석하면 int형 포인터를 요소로 2개 갖고있는 배열 pp로 해석되고
int (*pp)[2] 를 해석하기보다는 읽어보는게 나을거 같다. 읽어보면 다음과 같다.
pp라는 포인터를 선언을 하는데 가르키는 자료형은 int형이고 포인터 연산을 했을시
int형메모리크기(4byte)*2byte 만큼 건너뛴다 라고 표현할수 있다.
표현이 좀 어려웠나요 하지만 이렇게 이해하는것이 가장 쉬울거 같네요..
물론 이건 제기준입니다.^^:

[ 2차원배열 포인터타입의 간단한 예 ]
#include <stdio.h>

void show(int (*pp)[4], int a){
    int i,j;
    for(i=0; i<a; i++){
        for(j=0; j<4; j++){
            printf("%d",pp[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

int main(void){
    int array1[2][4] = {{1,2,3,4},{5,6,7,8}};
    int array2[3][4] = {{1},{2},{3}};

    show(array1, 2);
    show(array2, 3);

    return 0;
}

여기저기 굴러다니는 간단한 예제를 따와 봤습니다
테스트를 해보니 잘되더군요 이만한 예제가 없는듯해서.. 그리고 항상 깜밖깜밖잊는 내 머리를 위해서 준비한 예제지요^^:
신고
Posted by kyoe
。공부 。2008.01.17 23:07
배열이란 둘이상의 변수를 동시에 선언하거나 많은 양의 데이터를 일괄적으로 처리할때 주로 쓰인다.
하지만 우린 배열이또다른 의미를 갖고 있다는것을 알면서도 대충알고만 넘어가는 경우가 많다.

배열을 또다른 말로 풀이하면 상수포인터라고 말할수있습니다.
그이유를 알아보자.

int array[5] = {0,1,2,3,4};
다음과 같은 배열이 있고 배열 array의 첫번째 요소의 주소값이 0x1000번지에 할당되었다고 가정하자.
그럼 두번째 요소인 array[1]은 int형 배열이기 때문에 4byte를 건너뛴 0x004번에 할당될것이다. 만 double형이였다면 8byte씩 건너뛰어서 할당이 되겠지요..

아래와같이 배열요소의 결과값을 출력하면 결과값은 당연히 0과 1이 나오겠죠?
printf("array[0] : %d, array[1] : %d \n",array[0],array[1]);

그럼 이번엔 배열 요소의 주소값을 출력해 보죠. 결과값은 위에서 가정한 0x1000과 0x004 가 나올겁니다.
printf("%d 번지, %d 번지\n",&array[0],&array[1]);

마지막으로 배열이름의 주소값을 출력해 보도록하죠 이상하게도 위의 array[0]번째요소의 주소값인
0x000이 출력이 됩니다. 이것으로 우리는 배열의 이름은 배열의 첫번째요소 즉 배열의 0번째 인덱스의 주소값을 갖는것을 알수있습니다.
printf("array배열이름의 주소 : %d \n",a);

포인터로 예로 들자면 포인터 역시 주소값을 가르킬수있고 또한 자료형도 갖고 있습니다.
이렇게 우리는 주소값 + 자료형 을 갖고 있는것을 포인터라 하죠
배열역시 주소값과 자료형을 갖고 있기때문에 우리는 배열역시 포인터라고 정의를 내릴수 있습니다.

하지만 배열과 포인터가 완전히 같지는 않습니다. 포인터는 다른주소 의 값을 가질수 있지만 배열은 처음에 할당된 주소값만을 가르킬수있습니다. 만약 배열의 주소값을 변경할수있다면 백개씩이나 되는 배열을 요소를 갖고있는 배열을 실수로 다른 주소로 변경하게 된다면 난감하겠죠.. 뭐 간단한 예를 든겁니다. 꼭 이래서 그런것이 아니라.
실제로
int oop = 10;
array = &oop;
이렇게 array배열에 변수 opp의 주소값을 넣으면 에러가 발생합니다.

간단히 정리하자면 이렇습니다.
배열의 배열이름은 주소값과 자료형을 갖는 포인터이지만 주소값을 변경할수없는 상수포인터이다.

신고
Posted by kyoe
。공부 。2008.01.05 12:26
포인터를 사용하기 위해서 우리는 두가지 연산자를 사용한다. '*'와'&'가 그 두개다.
'*'는 포인터 변수를 선언할때도 쓰이지만 또다른 용도로도 사용한다.

- '&'의 쓰임 주소값 반환 -
int temp = 10;
int* pt1 = &temp;
printf("temp 의 주소값 : %d",&temp);
printf("pt1 의 값 : %d",pt1);

'&'는 주소값을 반환하는 용도로 쓰인다.
temp는 변수선언과 동시에 메모리상에 올라가게 되고 그와같이 주소값도 갖게 되는것이다.
예를 들어 temp의 주소값이 0x10이라면  int* pt1 = &temp; 이와같이 코드를 입력하면 temp의 주소값을 포인터pt1에 넣으라는 명령이 된다.
위의 코드에서 3,4번째 라인에 있는 결과값은 0x10되고 두개의 결과값이 동일하다는것을 알수있다.


- '*'의 쓰임 첫번째 포인터변수 선언 -
int* pt1;
char* pt2;
위와같이 사용하면 포인터변수를 선언하는것이다.
간단하게 읽어보면 int형 변수를 가르킬수있는 포인터 pt1 이렇게 읽을수있다.
char형도 읽는건 마찬가지다 하지만 여기서 포인터pt2는 char형만을 가르킬수있다.

- '*'의 쓰임 두번째  포인터가 가르키는 주소의'값' -

int temp = 10;
int* pt1 = &temp;
printf("*pt1 이 가르키는 값 : %d",*pt1);
int형 변수 temp를 상수10으로 초기화 하고 있다. 이 초기화된 값으로 포인터 pt1을 초기화 하고 있는데 포인터변수는 주소값으로 초기화 해야 하기때문에  '&'연산자를 이용해서 temp의 주소값으로 pt1을 초기화 하고있다.
printf()함수에서 포인터pt1이 가르키는 주소의 값을 출력하기 위해서 변수뒤에 '*'연산자를 붙여주고 있다 이렇게 '*'연산자는 포인터에서 두가지 용도로 쓰이는데 변수의타입을 정의하는 그러니까 int 앞에 '*'가 붙으면 포인터변수를 선언하는 것이고  변수의 타입 없이 변수뒤에 붙으면 포인터가 가르키는 값을 갖게 되는것이다.

[ 포인터와 이중포인터 예제 ]

void main(){
  
    int temp;
    int *p1;           //int형을 가르킬수있는 포인터
    int **pp1;      //int형 의 포인터를 가르킬수있는 포인터

    p1 = &temp;        //temp의 주소값으로 포인터p1을 초기화
    pp1 = &p1;        //포인터 p1의 주소값으로 포인터 pp1을 초기화
   *p1 = 10;         //'*'를 뒤에 붙였으므로 p1이 가르키는 temp의 값을 직접적으로 바꿀수있다.
                        //즉 temp = 10; 과 동일

    printf("자신들이 가리키는 값 출력\n");
    printf("temp = %d\n",temp);        //10출력
    printf("*p1 = %d\n",*p1);        //10출력    
    printf("*pp1 = %d\n",*pp1);        //p1의 값 출력(즉 temp의 주소값)
    printf("**pp1 = %d\n",**pp1);    //temp의 값 10출력

//-------------------------------------
    printf("자기자신의 주소값 출력\n");
    printf("&temp = %d\n",&temp);    //temp의 주소값 출력
    printf("&p1 = %d\n",&p1);
    printf("&pp1 = %d\n",&pp1);
//-------------------------------------
    printf("자신들이 가르키는 주소값 출력\n");
    printf("temp = %d\n",temp);    //temp 는 포인터가 아니기 때문에 temp의 값을 출력시켰다.
    printf("&p1 = %d\n",p1);        //temp의 주소값으로 초기화를 시켜줬으므로 temp의 주소값이 출력
    printf("&pp1 = %d\n",pp1);        //p1의 주소값으로 초기화를 시켜줬다. p1의 주소값출력

}
        - 결과 값 -
자신들이 가리키는 값 출력
temp = 10
*p1 = 10
*pp1 = 1245052
**pp1 = 10

자기자신의 주소값 출력
&temp = 1245052
&p1 = 1245048
&pp1 = 1245044

자신들이 가르키는 주소값 출력
temp = 10
&p1 = 1245052
&pp1 = 1245048


위예제를 간단히 설명하자면 아래와 같다.

포인터 pp1은 포인터p1의 주소값을 갖고 있기 때문에 포인터p1을 가르키게 되고
포인터p1은 temp의 주소값을 갖고 있으므로 temp를 가르키게 되겠죠.
[temp] <------- [*p1] <-------[**pp1]

만약 pp1 = &temp; 이렇게 &temp의 주소값을 포인터 pp1에 넣으면 에러가 발생한다.
포인터 pp1은 int형변수를 가르키는 포인터가 아니라 int형의 포인터를 가르킬수 있는 포인터이기 때문에 에러가 발생하게 되는것이다.

음... 말이 좀 어렵게 설명된거 같다...
신고
Posted by kyoe
TAG C, C++, 포인터

티스토리 툴바