C언어 보충4 - 15장

2018. 2. 3. 20:58

15장. 응용포인터

 

1. 단일 포인터

 - 포인터 변수의 값은 대상의 주소를 저장하고 포인터는 그대상을 가리킨다(의미한다)

 - 정의방법

    int a, b;

    ① int *pa=&a;

    ② int *pb;

        pb=&b;

    ③ *pa=10;

 - 포인터 사용방식

    ① (배열을 이용하지 않고)문자열을 사용할 때

     - ex1)

        char *pa="apple";

     - ex2)

        char *pa;

        pa="apple";

    ② 배열자체를 가리킬때(배열처럼 사용이 가능하다)

     - ex1)

        int ary[3];

        int *pa=ary;

     - ex2)

        int ary[3];

        int *pa;

        pa=ary;

    ③ 포인터 배열(=2차원 배열처럼 사용할 때) //배열의 요소가 모두 포인터인 배열

     - ex)

        int ary1[2];

        int ary2[2];

        int ary3[2];

        int *pa[3]={ary1, ary2, ary3}; //요소가 포인터(int *)임으로 들어가는 값은 주소이다

        pa[1][0]=10; //ary2[0]=10이다(=ary2배열의 첫번째 요소가 10이다)

        *(pa[0]+1)=20; //ary1[1]=20이다

     - 이해를 위한 비교

        pa[0]은 ary1을 의미하고, pa[0][1]은 ary1[1]을 의미한다

        더 정확히하면 pa[0]='ary1의 시작주소'이고 정수연산을 한다면 ary1[1]=*(ary1+1)=*(pa[0]+1)=pa[0][1]이다

    ④ (배열을 넘기기 위해)함수의 매개변수로서 사용

     - ex)

        int func(int *pa); //배열을 매개인자로 받기위해 포인터를 사용해 배열의 주소를 넘겨받음

        void main()

        {

          int ary[5];

          func(ary);

          ...

        }

 

 ※ 포인터를 이용한 배열의 경우

 - *(pa+0)=10; //배열의 첫번째요소를 10으로

 - pa[1]=20; //배열의 두번째요소를 20으로

 - scanf("%d", pa+2); //(배열 3번째 요소의 주소를 넣어) 배열의 3번째 인자값을

 이 3가지 모두 적용할 수 있다

 

 ※ 만일 함수의 매개변수 자리에 (배열을 인자로 받기위해)배열을 선언하면 배열명은 포인터로 바뀐다.

    ex) int func(int ary[5]) --> int func(int *pa)

    => 그대로 사용해도 문제는 없지만 이왕이면 포인터로 사용하자

 

※ 포인터 배열과 배열포인터간의 차이

 - 포인터배열은 배열의 요소가 모두 포인터인 배열이다.

    정의: int *pa[];

    위의 정의는 int *(pa[])와 같다
    배열 pa에 들어가는 요소는 모두 int *형이고, 이러한 변수에 들어가는 값에는 포인터가 가리키는 대상(int형)의 주소가 들어간다

    만일 포인터가 가리키는 대상이 int형 일반변수라면(int a라 가정) int *pa[0]=&a; 이다

    만일 포인터가 가리키는 대상이 int형 배열이라면(int ary[3]이라 가정) int *pa[0]=ary이다

 

 - 배열포인터는 배열을 가리키는 포인터를 의미한다.

    정의: int (*pb)[];

    배열 포인터는 보통 2차원 배열을 가리키는 포인터로 사용한다

    ex)

        int ary[3][4];

        int (*pa)[4];

        pa=ary;

        printf("%d", pa[1][2]);

         - *pa는 이차원 배열인 ary[3]을 가리키며 *pa의 자료형은 int[4]형이다. 마찬가지로 pa는 int[4] *형이다

         - 증명은 너무길어 하단의 상세증명으로 따로 적어두었다.

 - 상세증명

    int ary[3]; 이고 pa=ary일때

     - 1차원 배열의 이름은 첫번째요소의 주소(=배열의 시작주소)를 의미한다

     - 배열의 요소는 int형이다

     - pa는 요소의 주소이다(실제로 주소연산시 pa+1을 하면 배열의 2번째 요소를 가리킨다)

     - *pa가 배열의 요소를 의미하기때문에 *pa는 int형이고, pa는 int *형이다

 

    int ary[3][4]; 이고 pa=ary일때

     - 2차원 배열의 이름은 1차원배열의 시작주소(=2차원배열의 시작주소)를 의미한다

     - 2차원배열의 요소는 int형이다

     - 1차원배열의 요소는 int형 배열이다

     - pa는 1차원배열의 시작주소(=1차원 배열의 첫번째 요소의 주소)이다

     - *pa가 1차원 배열의 요소를 가리키기 때문에 *pa는 int형 배열형(정확히는 int[4]형)이고, pa는 int[4] *형이다 

 

=>증명에 관한 더 자세한 내용은 책과(459.p, 461.p~)필기자료를 찾아보자(c프로그래밍 필기자료 1~2주)

 

 

2. 이중포인터

 - 포인터를 가리키는 포인터

 - 포인터 사용방식

    ① (포인터를 넘기기 위해) 함수의 매개변수로서 사용

     - 보통 단일포인터를 넘기는 경우는 거의 없다(변수로 넘기지)

     - 대표적인 경우는 포인터를 사용해 문자열을 표현하여 서로의 연결을 해제할 수 없고, 이를 다른 함수에서 교체하고할경우나,

     - 하위블록에서 static변수를 사용하여(계속 살아있으니 접근가능, 하위 블록에서 return등으로 static변수의 주소를 넘긴다), 하위블록의 변수에 접근하고자 할 경우가 있다.(즉 매개변수로는 1가지..)

     - ex) 사실 책 451.p 예제를 보는것이 더 빠르다..

        char *b="abc";

        swap(&b);

        void swap(chat **c)

        {

          //포인터 주소 교체작업

        }

    ② (포인터 배열을 넘기기 위해) 함수의 매개변수로서 사용

     - 일반적인 일차원 배열을 넘길때는 단일포인터가 사용되나, 배열의 요소에 포인터가 있는 포인터배열은 이중포인터를 사용한다

     - ex)

        int *pa[3]; //포인터배열

        func(pa);

        void func(int **ppa)

        {

          ...

        }

    ③ (2차원 배열을 넘기기 위해) 함수의 매개변수로서 사용

     - 일차원 배열을 넘길때는 단일포인터가 사용되니, 2차원 배열 넘길때는 이중포인터사용

     - ex)

        int ary[3][4]; //이차원 배열

        func(ary);

        void func(int **ppa) //*ppa는 ary[3]이고 **ppa는 ary[3][4]이다.

        //좀더 자세히 보자면, ary[1][2]=*(ary[1]+2)=*(*(ary+1)+2)=*(*(ppa+1)+2)  [매개변수를 ary=ppa로 설정하였으니] 

        // =*(ppa[1]+2)=ppa[1][2]

        {

          ...

        }

    ④ (이중포인터는 아니지만) 2차원 배열을 넘기기 위해 배열 포인터를 함수의 매개변수로서 사용

     - 배열 포인터는 배열을 가리키는 포인터를 의미한다

     - 이러한 배열포인터는 보통 2차원 배열을 가리키는 포인터로 사용한다(상단의 ※ 포인터 배열과 배열포인터간의 차이 참조)

     - 일차원 배열을 함수로 넘기기위해 매개변수를 단일 포인터를 쓰듯, 2차원배열 또한 이중포인터 혹은 배열포인터를 이용한다

     - ex)

        int ary[3][4]; //이차원 배열

        func(ary);  //여기까지는 ③과 같다

        void func(int (*pa)[4]) //배열포인터 사용 (함수의 매개변수 pa=ary)

        {

          ...

        }

 

3. 함수 포인터

 - 임의의 함수를 지정하여 함수를 호출할 때(즉, 특정하게 함수를 지정하지 못할때). 혹은 함수정의 시점에서 함수를 결정하지 못하는 경우에 사용

 - ex)

    int sum(int a, int b);

    {

      return (a+b);

    }

    int (*fp)(int, int); //가리키는 대상이 함수임을 나타내기 위해 나타내기 위해 (*fp)를 해야한다.

    fp=sum; // 배열명 처럼 함수명도 시작주소가 될 수 있다

    result = fp(10,20);

   

4. void 포인터

 - 가리키는 자료형이 정해지지 않은 포인터

 - 보통 주소는 가리키는 대상의 자료형이 일치하는 포인터에만 대입이 가능하나, void포인터를 선언하는 것은 가리키는 자료형을 결정하지 않겠다는 의미이며, 따라서 모든주소를 저장 수 있다. 하지만 간접참조연산(ex: *vp)이나 정수연산(ex: vp+1)같은 주소연산이 불가능하다

 - ex) 일반 포인터와 void포인터 간의 차이

    int *ip  <-->  void *vp

    ip=&a  <-->  vp=&a

    *ip  <-->  *(int *)vp  //대상에 접근하기위해(간접참조연산)서는 형변환이 필요

    ip+1  <-->  (int *)vp+1  //정수연산위해 형변환 필요

 

 

 

 

 

'Study > 프로그래밍_언어' 카테고리의 다른 글

C언어 보충6 - 18, 19장, 추가사항  (0) 2018.02.03
C언어 보충5 - 16, 17장  (0) 2018.02.03
C언어 보충3 - 13, 14장  (0) 2018.02.02
C언어 보충2 - 11, 12장  (0) 2017.12.06
C언어 보충1 - 10장  (0) 2017.12.02

+ Recent posts