ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • class_lab10.cpp 포인터의 개념
    C++/class 2022. 5. 25. 17:47


    포인터의 주 사용 목적

    1. 용량이 큰 데이터를 전달하는 경우 -> 비용이 큼
    데이터 전달이 아니라 데이터 주소만 전달하기 때문에 시간, 메모리 등 비용적인 측면에서 효율적

    2. call by value
    함수의 매개변수로 어떤 값을 전달할 때 함수 내에서 매개변수를 수정하는 경우 변경되지 않음 (main함수 내에서는 변경O)
    ex) main 함수 내에서의 x,y -> 함수에서 x++, y++을 해도 돌아왔을 때 변화X
    그동안은 전역변수를 선언해서 사용하는 편법(?)을 이용했지만 포인터로 해결!


    #include <iostream>
    #include <iomanip>
    using namespace std;
    
    /*
     1. 포인터 변수란?
     주소를 가지고 있는 변수
     
     변수, 베열은 RAM(주기억장치)에 저장된다.
     int i의 메모리를 어디에 할당할지는(주소) 운영체제가 결정한다.
     
     [직접 참조] int i = 10;
     [간접 참조] int i가 4번지일 때, 4번지 = 10;
     
     4번지라는 주소를 알기 위해 필요한 것: 주소 연산자(&), 간접 참조 연산자(*)
     
     */
    
    
    
    
    //함수 원형
    void printArray(int x[], int size){
    
        
        for (int i = 0; i<5; i++)
            cout << setw(5) << x[i];
        cout << endl;
        
    }
    
    void intArray_i(int x[], int size){
        //배열을 {0,1,2,3,4}로 초기화
        for (int i = 0; i<5; i++){
            x[i] = i;
            cout << setw(5) << x[i];
        }
    }
    
    
    void intArray_p(int *x, int size){
        //배열을 {0,1,2,3,4}로 초기화
        for (int i = 0; i<5; i++){
            *(x+i) = i;
            cout << setw(5) << *(x+i);
        }
    }
    
    
    //함수 원형
    //함수 오버로딩: 원래 함수 이름이 같아도 매개변수만 다르면 OK였지만 포인터는 XXXX!!
    //생긴 건 다르게 생겼지만 int x[]도 주소고 int *x[]도 주소라서 컴파일러가 구분X
    
    void printArray2(int *x, int size){
        for (int i = 0; i<5; i++)
            cout << setw(5) << *(x+i);
        cout << endl;
        
    }
    
    
    
    int main(){
    
        int i = 10;     //운영체제가 주소를 정해줌 (ex. 4바이트, 4번지)
        char c = 69;    //1바이트, 8번지
        double f = 12.3; //8바이트, 12번지
        
        //주소는 고정된 값이 아니기 때문에 항상 알아봐야 한다. -> 주소 연산자(&) 필요
        //&i = 4;
        //*p = 10;
    
        //주소 출력
        cout << "i의 주소: " << &i << endl;    //1000
        cout << "f의 주소: " << &f << endl;    //2000
        
        //16진법으로 출력
        cout << "i의 주소: " << (long long)&i << endl;    //1000
        cout << "f의 주소: " << (long long)&f << endl;    //2000
        
        //i의 저장값
        cout << i << endl;      //10, 직접 참조
        cout << *(&i) << endl;  //10, 간접 참조, i의 주소를 먼저 찾고(&i)->거기에는 뭐가 있을까?*(&i)
        
        
        // ====================================================================
        
        
        //i의 주소는 매번 바뀜 -> 저장했다가 나중에 간접 참조를 하고 싶을 때
        //변수에 주소를 저장: 주소가 12자리이기 때문에 int는 탈락, long long 자료형의 변수 사용
        cout << "\n포인터의 선언" << endl;
        
        /*
        [에러] 주소를 일반 변수에 저장할 수 없음
         
        long long juso;
        juso = &i;      //변수에 i의 주소 저장
        */
        
        // 주소를 저장할 수 있는 변수 필요 -> 주소형 변수(포인터 변수)
        
        int *pi;    //정수형 주소를 저장할 수 있는 포인터 변수 pi 선언,  pi에는 정수형 주소만 저장할 수 있다
        
        // * 연산자: 곱셈, 간접참조, 포인터 선언 3가지로 선언
        
        pi = &i;    //포인터 변수에 i의 주소 저장,
        cout << "i의 저장값: " << i << endl;    //10
        cout << "pi의 참조값 *pi: " << *pi << endl; //10(i값)
        cout << "i의 주소의 참조값 *(&i): " << *(&i) << endl;
        cout << "pi의 저장값: " << pi << endl;  //i의 주소
        
        
        // ====================================================================
        
        
        //변수 i에 1 더하기
        
        i++;
        cout << "\ni + 1의 저장값: " << i << endl;
        
        i = i + 1;
        cout << "i + 1의 저장값: " << i << endl;
        
        i = *pi + 1;
        cout << "i + 1의 저장값: " << i << endl;
        
        *pi = *pi + 1;
        cout << "i + 1의 저장값: " << i << endl;
        
        (*pi)++;
        cout << "i + 1의 저장값: " << i << endl;
        
        
        // ====================================================================
        
        /*
         [주의사항]
         
         1. 포인터 타입과 저장하는 주소의 타입이 일치해야 한다.
         ex)
         char c;
         pi = &c; // pi는 정수형 포인터 변수라서 할당할 수 없다.
         
         2. 포인터(초기화) 누락 -> 문법 상 문제는 없지만 쓰레기값 출력
         int *p;
         *p = 100;
         
         3. NULL 주소 참조
         int *p  = NULL;    //비어있는 주소로 초기화
         *p = 100;
         
         */
        
        
        // ====================================================================
        
        //포인터의 크기
        cout << "\n정수형 변수 i의 크기: " << sizeof(i) << endl;  //4
        cout << "정수형 변수 c의 크기: " << sizeof(c) << endl;  //1
        cout << "정수형 변수 f의 크기: " << sizeof(f) << endl;  //8
        
        double d;
        char *pc = &c;
        double *pd = &d;
        
        cout << "\n문자형 포인터 pc의 크기 " << sizeof(*pc) << endl;  //
        cout << "정수형 포인터 pi의 크기: " << sizeof(*pi) << endl;  //
        cout << "double형 포인터 pd의 크기: " << sizeof(*pd) << endl;  //
        
        
        // ====================================================================
        
        //포인터의 연산
        cout << "\n포인터의 연산" << endl;
        
        //포인터++의 의미?
        //=>다른 데이터의 주소! **번지수를 1 증가 시키라는 얘기가 아님!
        
        // pi = 100,
        // pi + 1 = 104,
        // pi - 1 = 96
        
        float *pf = (float *)100;
        pc = (char *)100;
        pi = (int *)100;
        pd = (double *)100;
        
        cout << "pc의 저장값: " << (long long)pc << endl;    //문자형 100번지
        cout << "pi의 저장값: " << (long long)pi << endl;    //정수형 100번지
        cout << "pf의 저장값: " << (long long)pf << endl;    //플롯형 100번지
        cout << "pd의 저장값: " << (long long)pd << endl;    //더블형 100번지
        
        
        cout << "포인터 연산 => 포인터에 ++(증감 연산자)를 했을 때" << endl;
        pc++;
        pi++;
        pf++;
        pd++;
        
        cout << "\npc의 저장값: " << (long long)pc << endl;   //문자형 101번지
        cout << "pi의 저장값: " << (long long)pi << endl;    //정수형 104번지
        cout << "pf의 저장값: " << (long long)pf << endl;    //플롯형 104번지
        cout << "pd의 저장값: " << (long long)pd << endl;    //더블형 108번지
        
        
        // ====================================================================
        
        
        // @@이해 필요
        // 112 - 108 = 8이 아니라 112과 104 사이에는 정수가 두 개 있으므로 결과는 2
        int *pi2 = (int *)112;
        cout << "pi2 - pi = " <<pi2 - pi << endl;       //112 - 104 => 8???
        
        
        // ====================================================================
        
        
        // 포인터와 증감 연산자
        
        int j;
        
        i = 3;
        pi = &i;
        
        cout << "\npi의 저장값: " << (long long)pi << endl;
        j = *pi++;
        
        cout << "\nj = *pi++ 실행 후 " << endl;
        cout << "i의 저장값: " << i << endl;    //3
        cout << "j의 저장값: " << j << endl;    //3
        cout << "pi의 저장값: " << (long long)pi << endl;   // +4번지만큼 증가
        
        // ====================================================================
        
        i = 3;
        pi = &i;    //초기 상태로 복구
        
        j = (*pi)++;
        
        cout << "\nj = (*pi)++ 실행 후 " << endl;
        cout << "i의 저장값: " << i << endl;    //4
        cout << "j의 저장값: " << j << endl;    //3
        cout << "pi의 저장값: " << (long long)pi << endl;
        
        
        
        // ====================================================================
        
        i = 3;
        pi = &i;    //초기 상태로 복구
        
        j = *++pi;
        
        cout << "\nj = *++pi; 실행 후 " << endl;
        cout << "i의 저장값: " << i << endl;    //3
        cout << "j의 저장값: " << j << endl;    //쓰레기값
        cout << "pi의 저장값: " << (long long)pi << endl;   // +4번지만큼 증가
        
        
        // ====================================================================
        
        i = 3;
        pi = &i;    //초기 상태로 복구
        
        j = ++*pi;
        
        cout << "\nj = ++*pi; 실행 후 " << endl;
        cout << "i의 저장값: " << i << endl;    //4(참조값 증가)
        cout << "j의 저장값: " << j << endl;    //4
        cout << "pi의 저장값: " << (long long)pi << endl;
        
        
        // ====================================================================
        
        // 배열과 포인터 [오늘의 핵심]
        /*
         함수에 배열 전달
         함수 호출: 함수명(배열명[], ...)
         함수를 호출할 때 배열의 크기를 쓰지 않는 이유 -> 배열명이 배열의 시작 주소이기 때문이다.
         
         !!! 배열명 = 배열의 시작 주소
         
         배열명은 포인터다.
         포인터는 주소를 바꿀 수 있지만,
         배열명은 배열의 시작 주소를 항상 알려줘야 하기 때문에 const pointer여서 수정 불가
         
         */
        
        int point[5] = {1,2,3,4,5};
        
        cout <<"\n배열의 시작주소는 제일 앞 원소의 주소(&point[01]) = " << (long long)&point[0] << endl;
        
        cout << "배열의 시작주소는 배열명(point) = " << (long long)point << endl; //소오름...
        
        //*(point+i) = point[i]
        // 주소로 표현  = index로 표현
        
        //예시
        //point = (int *)100; -> 배열명이 const pointer여서 오류
        
        cout << "\n*(point + 0) = " <<*(point + 0) << endl;     //1;
        cout << "point[0] = " << point[0] << endl;             //1;
        
        cout << "*(point + 1) = " <<*(point + 1) << endl;     //2;
        cout << "point[1] = " << point[1] << endl;             //2;
        
        
        //배열 전체 출력
        cout << "\n배열 전체 출력(index+loop): " << endl;
        for (int i = 0; i<5; i++)
            cout << setw(5) << point[i];
        cout << endl;
        
        
        cout << "\n배열 전체 출력(포인터): " << endl;
        for (int i = 0; i<5; i++)
            cout << setw(5) << *(point+i);
        cout << endl;
        
        
        // ====================================================================
        
        
        // 포인터와 함수
        // 배열을 함수에 포인터로 전달하는 방법
        
        //배열 전체 출력
        cout << "\n배열 전체 출력(index+function): " << endl;
        //printArray(point[5], 5);          //X
        //printArray(point[], 5);           //X
        printArray(point, 5);
        
        
        cout << "\n배열 전체 출력(포인터+function): " << endl;
        //printArray(&point[], 5);        //OK
        printArray2(point, 5);
        
        
        
        
        //배열을 {0,1,2,3,4}로 초기화(배열 이용)
        for (int i = 0; i<5; i++){
            point[i] = i;
        }
        
        cout << "\n배열의 초기화: " << endl;
        printArray(point, 5);
        
        //배열을 {0,1,2,3,4}로 초기화(포인터 이용)
        for (int i = 0; i<5; i++){
            *(point + i) = i;
        }
        printArray(point, 5);
        
        intArray_i(point, 5);       //배열
        cout << endl;
        intArray_p(point, 5);       //주소
        cout << endl;
        
        // 시간 되면 2차 배열과 포인터
        
        // 과제 설명
        //1. lab8의 함수 원형을 다 포인터로 바꾸기
        //int x[] -> int *x
        
        //merge 함수는 할 필요X, 함수 원형, 함수 선언만 바꾸기
        //main 함수도 손 댈 일이 없음(배열명이 배열의 주소였기 떄문에 호출에는 수정사항X)
        
        return 0;
    }

Designed by Tistory.