ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Understanding and Using C Pointers(기억이 잘 안나는 부분)
    @ 16. 1 ~ 17. 1/C++ 2015. 6. 28. 01:49

    1. 포인터 선언을 읽는 방법

    포인터 선언을 뒤에서부터 읽음으로써 선언을 점차적으로 이해할 수 있음.

    const int *pci 이것은??

    (뭐 지금까지 공부한 내용으로는 const *왼쪽에 있으니 int* 값이 상수다..이미 결론이 나오지만..)

    (*pci=값 수정 안됨)

    const int *pci; // 변수 pci

    const int *pci // 포인터 변수 pci

    const int *pci //정수를 가리키는 포인터 변수 pci

    const int *pci //상수 정수를 가리키는 포인터 변수 pci

     

    2. 정수와 포인터를 저장하는데 같은 크기의 바이트를 사용하지만..이 둘은 같은 데이터 타입이 아니다.

    그러나 정수를 정수 포인터로 캐스팅하는 것은 가능하다..

    int *pt;

    int num=1;

    pt=(int*)num;

    num이 pt의 주소값으로 들어가게됨...00000001

    pt를 역참조시 오류가 남..

     

     

    3. void 포인터 : 어떤 타입의 데이터도 참조할 수 있는 범용 포인터다.

    void* pv;

    모든 포인터는 void 포인터에 할당될 수 있으며 할당된 포인터는 다리 원래의 타입으로 캐스팅해서 사용가능함..(기존값과 같게됨)

    int num = 10;
     int *pt = #
     cout << *pt;
     void *ptt = pt;
     int *ppt;
     ppt = (int*)ptt;
     cout << *ppt;

    결과값은 10 10

    void포인터는 데이터 타입의 포인터에만 사용되고 함수포인터에는 사용안됨

     

    4. 포인터의 크기 확인 sizeof(int*) //나의 운영체제에서 int* 포인터의 값은 크기가 어떻게 되지?

     

    5. 포인터에 정수 더하기 빼기

    실제 더해(빼)지는 양은 포인터가 가리키는 데이터 타입의 크기에 더해(빼)진 정수를 곱한 만큼이 된다.

    정수형 포인터에 4를 더했다면 실제로는 4(정수형 포인터 크기) * 4(더해진 상수) = 16바이트값으로 감..

    * 포인터 빼기는 두 포인터 주소 사이의 차를 얻기위해..배열 요소의 순서를 결정짓는 정도로 이용??

     

    6. 이중 포인터

     char *titles[] = { "A", "B", "C", "D" }; //전체 책 제목 목록을 포함하는 문자열 배열
     //아래는 추가적인 2개 배열
     //두 배열은 titles배열로부터 책 제목을 복사하여 가지는것보다
     //titles배열에서 해당 책 제목의 주소를 포함하여고 함!(주소!!)
     //이렇게 하려면 이 두 배열은 char포인터에 대한 포인터로 선언되어야함.
     
     //1. 전체 책 목록 중 가장 잘팔리는 책
     char **bestBooks[3];
     //2. 전체 책 목록 중 영어책 목록
     char **englishBooks[4];

     //사용법
     bestBooks[0] = &titles[0]; //*titles의 요소중 주소이니..
     
     englishBooks[0] = &titles[0];

     

    7. 상수 정수를 가리키는 pci포인터 변수의 선언(const int* pci)의 의미

     const int *pci;

     const int num2 = 5;
     pci = &num2; //1

     int num = 10;
     pci = &num;//2

     cout << *pci << endl;//3

     *pci = 100;//4

    1) pci는 다른 상수 정수를 가리키도록 할당될 수 있다.

    2) pci는 다른 비상수 정수를 가리키도록 할당될 수 있다. //...

    3) pci는 가리키는 정수의 값을 읽기 위해 역참조 될 수 있다.

    4) pci는 가리키는 정수의 값을 변경하기 위해 역참조 될 수 없다.

     

    8. 메모리 주소 손실

    포인터가 해제되지 않고 새 메모리 주소로 재할당되는 경우 기존 주소는 손실되며

    메모리 누수로 이어짐..

    int* pt=new int;

    *pt=5;

    pt=new int;

    이렇게 될 경우 첫번째 5가 들어간 주소는 (예를들어 100이라고하면)100의 메모리 주소는 손실로 이어짐

     

    9. 댕글링 포인터

    메모리 해제 후 해제된 메모리에 접근

    함수 호출에서 자동 변수를 가리키는 포인터의 반환???????????????????????????

    어쨋든 메모리를 해제한 후에도 변수에는 여전히 메모리의 주소를 가리키고 있음..

    *********예전 C의 malloc과 free사용시에는 댕글링 포인터가 발생되는데..

    +++++++++C++의 new와 delete에는 delete이후에 가리키는 주소값이 쓰레기값으로 들어가버리게 되어 더이상 접근 안됨

     

    10. 함수로부터 포인터를 반환할 때 잠재적인 문제점

    1) 초기화되지 않은 포인터의 반환

    2) 잘못된 주소를 가리키는 포인터의 반환

    3) 로컬 변수를 가리키는 포인터의 반환

    4) 반환된 포인터의 메모리 해제 실패(함수 호출자에게 할당된 메모리를 해제할 책임이 있음!)

     

    11. 포인터의 포인터 전달~

    포인터가 함수로 전달되면 포인터는 값에 의해 함수로 전달된다.

    void 함수(int *num);

     

    함수내에서 포인터의 복사본에 대한 수정이 아닌 포인터 자체의 수정을 원한다면 포인터의 포인터를 전달해야함

    (복사본은 *이것을 이용해서 수정가능하나..포인터 자체의 수정 즉 주소값을 변경하는것..포인터가 가리키는 주소값!)

    (호출하는 함수에서 할당된 포인터를 수정(포인터 자체 수정하는것..값이 아니라)하려면 호출 시 포인터의 주소를 전달해야함..그러려면..포인터의 포인터로 선언이 되어야함.. 그리고 전달해주는 쪽은 포임터의 주소를 전달해야함..)

    int *ptr

    ptr=&num;

    ptr  //값은 ptr이 가리키는 num의 주소값 //이 값을 변경하려면 이중포인터....

    *ptr  //num이 가지고 있는 값

    &ptr //ptr이 할당받은 주소값

     

     

    그러니까 일반 swap 함수의(int num1, int num2)예제와 포인터 swap(int* num1, int* num2)은 일반 int 값에 대한 함수인데..일반int값이 아니라 int*라면..??

    swap(int*num1,int*num2)는 위의 일반swap과 같고..swap(int**num1, int**num2)는 포인터 swap와 같다는것..

     

    11. 함수 포인터 이름규칙에는 항상 f 접두사를 붙임..

    void(*fptr)();

    함수 이름이 즉 주소이니..주소연산자가 불필요함..(근데 있어도 무방)

    fptr=square;

    fptr=&square;

    typedef를 써서 하면 더 편리함..

    typedef void(*fptr)();

    fptr fpt[10]={}; //이렇게 함수포인터 배열도 가능..

     

    12. 포인터와 배열

    *(pv+i) 는 vector[i]와 같다

    pv + i(주소반환)는 &vector[i]와 같음

    pv는 포인터 lvalue이고..vector는 배열 이름은 그 값을 수정할 수 없으므로..lvalue가 아님

    pv = pv + 1; //에러아님

    vector = vector + 1; //에러

     

    int (*pmatrix)[5] = matrix;

    *pmatrix는 배열에 대한 포인터를 선언..행당 다섯개의 정수 요소를 가진 2차원 배열에 대한 포인터로 정의(2차)

    괄호가 없다면 다섯개의 정수에 대한 포인터를 요소로 가진 배열이 된다.(1차)

    여기서 matrix 와 matrix + 1의 차이는..

    matrix + 1은 (int 배열일 경우) 배열의 시작으로부터 4만큼 떨어진 주소가 아니라 첫번째 열의 크기인 20바이트 떨어진 값이 된다.(int matrix[2][5]일경우)

    matrix[0] 와 matrix[0] + 1과는 틀림..(이건 첫번째 열의 첫번째 요소의 주소를 반환하고 거기에 + 1의 정수하나의 크기가 더해진것..[0][1]에 접근)

     

    matrix[i][j] = matrix의 주소 + (i * 열크기) + (j * 요소의 크기)

    arr + (i*행) + j

     

    2차원 배열의 함수 매개변수 표현법

    int arr[][5] //행을 알때..

    int (*arr)[5]  //행을 알때..

    int *arr //행과 열을 매개변수로 같이 받을때..

    (이때 매개변수는 정수 포인터로 받는데.. matrix(2차배열)는 안됨 정수의 배열에 대한 포인터이니까..

    그렇다면 matrix[0] 또는 &matrix[0][0] 이것이 되어야함..)

    (함수 내부에서 arr[i][j]사용 못함..왜냐면 차원의 크기를 모르니까..)

    (함수 내부에서는 arr + (i*행) +j를 사용..왜냐면 실제 메모리는 한줄로 쭈욱 되어있으니..주소값을 받아 쭈욱 간다)

     

     

    13. 불연속 메모리

    2차원 배열이 동적할당이 되면 해당 행은 연속적이나 열과는 불연속 적이다.

     int **ptr = new int*[2];
     *ptr = new int[10];

     for (int i = 0; i < 2; i++)
     {
      for (int j = 0; j < 10; j++)
      {
       cout << &ptr[i][j] << endl;
      }
      cout << endl;
     }
     cout << endl;

     int arr[2][10];
     for (int i = 0; i < 2; i++)
     {
      for (int j = 0; j < 10; j++)
      {
       cout << &arr[i][j] << endl;
      }
      cout << endl;
     }

    이 값의 arr은 연속적인 메모리 할당..ptr은 행단위 연속적인 메모리 할당..

    ptr[0][10]과 ptr[1][10]은 다른메모리 주소로 할당이 되어있다고..

    그럼 어떻게 해야 같은 메모리 주소를 인접하게 할당받나..

     

    int **ptr = new int*;
     ptr[0] = new int[20]; 이런식으로 하면됨..

    1차원을 2차원으로 늘려서 해야하.... 아 이건아닌듯..

    아니면 int*ptr = new int[rows*col*sizeof(int)] 이렇게 하라는데..아..

     

     

    14. 문자열 포인터

    문자열 리터럴(문자열 상수) : 큰 따옴표로 둘러싸인 문자의 배열(이것은 문자열 리터럴 풀에 위치함)

    문자열 리터럴은 함께 사용되어 null로 끝나는 문자열을 형성하는 문자 시퀀스를 나타냅니다. 문자를 큰따옴표로 묶어야 합니다. 다음과 같은 종류의 문자열 리터럴이 있습니다

    const char *narrow = "abcd";

    // represents the string: yes\no
    const char *escaped = "yes\\no";

    문자열 리터럴은 상수이므로 이를 수정하려고 하면(예: narrow[2] = 'A') 컴파일러 오류가 발생합니다

     


     

    이외 초기화할때 발생하는것...

    char header2[];

    header2="MEdia" 이건 아님..문자열의 주소를 배열 변수의 이름에 할당이 안됨..

    초기화할떄 하던가(char header2[]="MEdia") 공간을 주고 strcpy하던가 (char header2[10]; strcpy(header2, "MEdia"), 공간주고 하나씩 넣던가 (header2[0]='M';)

     

    두개의 문자열을 비교할떄 사용되는 잘못된 방법

    char str[10]

    if(str=="abc")

    이것은 str주소와 문자열 상수의 주소를 비교하는것임..값을 비교하는게 아니라..

     

    하나의 포인터를 다른 포인터에 할당한다고 하여 문자열이 복사되는 것은 아니다.

    이는 단순히 문자열의 주소를 복사한 것 뿐..

    char *str[30];

    str[29]="abc";

    str[30]=str[29];

    포인터만 복사됨..문자열은 복사 안됨 같은 리터럴 문자열을 참조함..

     

    함수 안에서 문자열을 반환하려고 할때. 2가지

    1) 정보를 채울 빈 버퍼를 전달하고 함수가 버퍼를 다시 반환(함수를 호출하는 쪽에서 버퍼를 해제)

      (1) 버퍼의 주소와 크기를 전달해야함

    2) 함수 내에서 동적으로 버퍼를 할당 반환(이것은 함수를 호출하는 쪽에서 함수가 어떻게 사용될지 완전히 이해해야함. 메모리 누락 땜시..)

    보통은 포인터로 매개변수, 반환 다 함..(문자열 주소)

     

    구조체(클래스)에 메모리가 할당되는 방법

    class A
    {
    public:
     int a;
     int b;
     int c;
    };

    class B
    {
    public:
     int a;
     int b;
     short c;
    };

    이런식으로 확인해보면..sizeof 시

    A와 B둘다 12바이트 임..이런것을 패딩이라고 함..(종종 필드크기의 합보다 더 많은 크기가 할당되는것..)

     

     

    15. 포인터의 오남용

    int* ptr1, ptr2

    이 선언을 무엇을 뜻하나?? 모두 int*형식이 아니다

    ptr1=int*  형식 ptr2=int 형식이다

     

    define보다는 typedef를 쓴다

    #define PINT int* (별로..)

    typedef int* PINT (긍정)

     

    if(ptr==NULL) 보다는

    assert(ptr!=NULL); 사용한다 (assert.h에 선언)

    결과가 true이면 아무일도 없음...false이면 프로그램 종료

Designed by Tistory.