리메이크 중/C,C++ 이론 중심

C(&C++) 이론 17. 포인터 1

라이피 (Lypi) 2023. 1. 8. 18:47
반응형

 


 

내용 참고

C++ 기초플러스 4판 (성안당)

혼자 연구하는 C/C++ (Soen.kr/와우북스)

Microsoft Docs (구 MSDN)


포인터

■  포인터란 컴퓨터의 메모리 주소에 직접 접근하여 데이터를 처리할 수 있는 문법이다.

■  메모리에 직접 접근하기 때문에 사용자의 부주의로 인한 에러가 일어나기 쉬우니 조심해야 한다.


Ⅰ. 포인터의 정의

ⅰ. T형 포인터

■  임의의 자료형인 T형 자료형을 저장하고 있는 메모리의 주소를 나타내는 포인터를 T형 포인터라고 한다.

■  예를들어 int값을 저장하고 있는 메모리를 나타내는 주소를 저장한 포인터는 int형 포인터라고 한다.

■  임의의 T형 변수가 있다면 언제나 임의의 T형에 대한 포인터도 선언할 수 있다.

■  포인터 변수를 선언할 때 사용하는 연산자는 * 이다. 

■  포인터 변수를 선언하는 방법은 띄어쓰기에 따라 4가지 형태가 있다.

■  1) 자료형* 변수명, 2) 자료형 *변수명, 3) 자료형 * 변수명, 4) 자료형*변수명.

■  사람에 따라 다르겠지만 주로 1)번이나 2)번 형태를 많이 쓴다. 

■  1)번 형태는 자료형에 대한 포인터임을 강조할 때 쓰고, 2)번 형태는 이 변수가 포인터 변수임을 강조할 때 주로 쓴다.

■  C 스펙 문서는 모두 1)번 형태로 되어 있고, C++ 스펙 문서는 2)번 형태가 조금 더 많다. MFC는 모두 2)번 형태이다.

■  필자는 1)번 형태를 주로 사용하나, 포인터형 변수를 동시에 여러개 선언할 때는 2번 형식이 필요하다.

■  int* i, j; (i는 정수형 포인터, j는 정수형 변수) // int *i, *j; (i,j 모두 정수형 포인터)

 

728x90

 

ⅱ. 포인터 사용하기 기초

#include <iostream>
using namespace std;

int main() {
    int a = 1;
    int b = 2;
  
    int* ip = &a;
    cout << *ip << endl;
    ip = &b;
    cout << *ip << endl;
}

 

■  int형 변수 2개를 선언한 뒤 값을 초기화하고 int형 포인터를 만들어 첫번째 변수의 주소를 넣었다.

■  &은 변수의 주소를 반환하는 연산자이다. scanf() 함수를 다룰 때 본적이 있을 것이다.

■  이를 이용하여 int형 변수가 아닌 포인터로 변수의 값을 출력했다. 첫번재 변수의 값이 나온다.

■  포인터 이름 앞에 사용한 *는 포인터 변수가 가리키는 주소에 저장된 값을 반환하는 연산자이다.

■  그  다음 포인터 변수에 두번째 변수의 주소를 저장했다.

■  똑같이 포인터 변수가 가리키는 주소에 저장된 값을 반환하지만 이번에는 두번째 변수의 값이 나오는 것을 확인할 수 있다.

■  포인터를 이용하면 변수 안에 저장된 값은 건드리지 않고도 포인터 변수의 주소만 변경하여 두 값을 모두 출력할 수 있다.

 

ⅲ. 포인터 사용시 주의사항

■  포인터가 가리키는 값을 '대상체(Object)'라고 한다.

■  32비트 환경을 기준으로 포인터 변수의 크기는 4바이트이다. 

■  64비트 환경을 기준으로 하면 포인터 변수의 크기가 8바이트가 된다. 

■  왜냐하면 32비트 환경에서는 주소값을 4바이트로 저장하고 64비트 환경에서는 8바이트로 저장하기 때문이다.

■  대상체의 타입은 달라도 주소를 저장하는 데 필요한 공간은 동일하기 때문에 포인터 변수의 크기는 모두 동일한 것이다.

■  하지만 대상체의 타입이 다른 포인터 변수끼리는 서로 호환되지 않는다.

■  예를들어 int형 포인터와 double형 포인터가 가리키는 값을 바꾸면 제대로 표현되지 않는다.

■  왜냐하면 대상체의 타입에 따라 값을 해석하는 방식이 다르기 때문이다.

■  대신 대상체의 자료형이 같은 포인터끼리는 주소를 서로 교환하거나 대입할 수 있다.

■  포인터끼리는 더할 수 없지만 뺄 수는 있다. 포인터끼리 뺀 값의 데이터 타입은 정수형이며 정확한 타입은 컴파일러에 따라 다르다.

■  왜냐하면 포인터끼리 더한 값은 논리적으로 아무런 의미가 없고,

■  포인터끼리 뺀 값은 메모리 상에서의 상대적 거리로 논리적인 의미를 갖기 때문이다.

■  포인터와 실수의 연산은 허용되지 않는다. 주소값은 순서이기 때문에 정수 범위에서만 의미가 있기 때문이다.

■  포인터끼리 곱하거나 나눌 수 없다.

■  포인터끼리 비교할 수 있다. 주로 NULL 포인터와 비교하여 현재 대상체가 있는지 확인하는데 사용한다.

 

ⅲ. 포인터끼리의 연산 핵심 예제

#include <iostream>
using namespace std;

int main()
{
    int ar[] = {1,2,3,4,5};
    int *p1, *p2, *p3;

    p1 = &ar[0]; //배열의 첫 요소
    p2 = &ar[sizeof(ar)/sizeof(ar[0])-1]; //배열의 마지막 요소

    //이 때 배열의 가운데 값을 구하고 싶다면

    //p3 = (p1+p2)/2;
    //이는 포인터끼리의 연산이 불가능하므로 쓸 수 없다. 
    
    p3 = p1+((p2-p1)/2);

    //이렇게 쓰면 p3에 배열의 가운데 값이 들어간다.
    //왜냐하면 p2-p1은 배열의 마지막 요소에서 첫번째 요소 사이의 거리이고,
    //이 값을 2로 나누면 첫번째 요소로부터 가운데 요소까지의 거리가 되기 때문이다.
    //그러므로 이 값을 p1에 더해주면 배열의 정가운데의 요소를 가리키게 된다.

    cout << ar << endl;
    cout << "p1 : " << *p1 << ", p2 : " << *p2 << ", p3 : " << *p3 << endl;
}

 

Ⅱ. 포인터와 배열

ⅰ. 배열의 이름은 포인터다.

■  배열을 상당히 나중에 포인터 직전에 포스팅한 이유가 있다.

■  배열은 연속된 메모리이기 때문에 포인터와 밀접한 연관이 있기 때문이다.

■  배열의 이름은 배열의 첫번째 위치를 나타내는 포인터 상수이다.

■  포인터 상수이기 때문에 저장된 주소값을 바꿀 수는 없다.

■  하지만 sizeof()로 계산하면 배열 전체의 크기를 리턴한다.

#include <iostream>
using namespace std;

int main()
{
    int arr[5] = {0,10,20,30,40};

    cout << "arr[2] = " << arr[2] << endl;
    cout << "*(arr+2) = " << *(arr+2) << endl;
    cout << "*arr + 2 = " << *arr + 2 << endl;
    
    return 0;
}

■  포인터에 정수를 더하면 포인터가 가리키고 있는 주소에서 상대적으로 정수값만큼 이동한 주소이 된다.

■  이는 배열의 이름에 정수를 더하면 인덱스로 그만큼 이동한 값의 주소가 된다는 뜻이다.

■  위의 예시를 보면 arr[ X ]는 *(arr + X)와 같은 결과가 나온다. *arr + X와는 다른 값이 나온다.

■  이를 통해 arr[ X ] 는 *(arr + X) 과 같은 의미임을 알 수 있다. (괄호에 주의해야 한다.)

■  먼저 arr은 arr배열의 첫번째 값 (=0)을 가리키는 포인터이고, arr+X는 첫번째 값으로부터 X만큼 이동하라는 뜻이기 때문이다.

■  이 주소를 가리키기 위해서 괄호를 치고 *연산자로 값을 취하면 arr[X]와 같은 결과가 나오는 것이다.

■  하지만 *arr은 바로 첫번째 값을 리턴하기 때문에 괄호가 없으면 결과가 달라진다.

■  T형 포인터 변수 px에 정수 i를 더하면 px = px + (i * sizeof(T))가 된다.

 

반응형

ⅱ. [ ] 연산자의 의미

■  이전에 연산자를 다뤘을 때 살짝 나왔지만 [ ] 도 연산자이다.

■  ptr이 임의의 배열을 가리키는 포인터이고 n이 정수라면 ptr[n] = *(ptr+n) 이다.

■  그 결과 1) ptr[n]  2) *(ptr+n) 은 같으며 심지어 3) n[ptr]도 가능하다.

 

 


반응형