삶의 공유

[C++] Pointer(포인터)[상] 본문

Programing/C++

[C++] Pointer(포인터)[상]

dkrehd 2021. 12. 25. 18:32
728x90
반응형

이번 포스팅에서는 포인터에 대해 알아보자 !

 

정의

 - 변수를 가리키는 변수

 - 메모리 주소를 가리키는 변수

 - 메모리 주소를 저장하고 있는 변수

이다. 일반적으로 * 키워드를 사용하고 있는 변수라고 보면 된다. 아래와 같이 pointer변수의 value값과 num변수의 주소 값이 같은 것을 볼 수 있다

 

예제(포인터)

위의 내용을 코드로 나타내면 다음과 같다.

※여기서 &연산자는 단향일 경우는 주소를 나타내는 연산자이다. 다항일 거유는 비교구문으로 and표시로 사용

int num = 10;
int* pointer = #

그럼 이 결과가 어떻게 되는지 한번 살펴보자.

 

#include <iostream>
using namespace std;

int main()
{
    int num = 10;
    int* pointer = &num; 
    
    cout << &num << endl;
    cout << pointer << endl;
    
}

 

다음과 같이 주소가 동일 하게 나오는 것을 볼 수 있다.

이번에는 값에 대해 접근을 해보자. 포인터 변수에서 주소가 아닌 값에 접근하기 위해서는 *연산자를 한번 더 사용하는 꼴로 역참조를 한다.

코드를 보면

cout << num << endl;
cout << *pointer << endl;

다음과 같이 10을 출력하므로 잘 접근 했다는 것을 볼 수 있다. 

여기서 포인터는 해당 변수의 주소값을 가르키고 있다고 했다. 포인터 변수가 역참조를 해서 해당 값에 접근한다는 것은 주소를 거쳐서 해당 값에 접근하는 것이라고 보면 된다 

 

즉 그래서 역참조 하여 이 값을 바꾸게 되면 해당 주소는 동일한 값을 변경하는 것이기 때문에 값이 변경이 된다. 

코드로 예를 들어 보자

int main()
{
    int num = 10;
    int* pointer = &num; 
    
    cout << *pointer << endl;
    
    *pointer = 100;
    
    cout << num << endl;
    cout << *pointer << endl;
}

자 다음과 같이 값이 바뀌는 것을 확인 할 수 있다.

여기서 우리가 확인 할 수 있는 것은 

 

포인터는 주소값을 넣어주는 변수다!

이 내용만 잘 기억하고 있자 !

 

 

포인터는 주소값을 갖는 변수인데 타입이 왜 필요할까? 라는 의문을 가질수 있다.

결론부터 얘기하면 역참조에서 가르키는 주소의 값을 읽을때 어떤 타입으로 읽을지를 결정하는 부분이다!

 

만약 포인터를 주소가 아닌 값을 넣으면 어떻게 될까?

한번 실험해보자

int main()
{
    int* pointer = 10;
    cout << pointer << endl;
    return 0;
}

다음과 같이 error를 발생시킨다. 내용을 보면 int를 int*로 변환 시킬 수 없다는 내용이다.

 

포인터에 const 키워드를 넣어서 활용하는 부분을 공부해보자

아래 코드를 봐보자

int main()
{
    int num = 10;
    const int* pNum = &num;
    
    int num0 = 20;
    pNum = &num0;
    
    cout << *pNum << endl;
    
    return 0;
}

결과를 보면 다음과 같다.

const를 넣었는데 pNum의 주소값이 왜 바뀌지 라고 궁금해할수 있다. 

const int* pNum = &num;

이거의 의미는 pNum을 가지고 num을(값) 바꿀수가 없다는 의미다. 즉 위의 코드는 num을 변경한 것이 아니라 pNum의 주소값이 num0의 주소값으로 바뀐 것이기 때문에 컴파일이 될 수 있었다. 

 

만약 const의 위치가 라고 한다면 주소값을 바꿀수 없다는 의미로 위의 예제 코드는 컴파일이 되지 않을 것이다.

int* const pNum = &num;

 

포인터와 배열과의관계

 

정수 배열을 만든 뒤 출력을 해보자 과연 어떤 값이 나올까

using namespace std;

int main()
{
    int num[] = {1,2,3};
    
    cout << num << endl;
    
    return 0;
}

이렇게 주소값이 나오는 것을 볼 수 있다.

 

그럼 이렇게 생각해볼수 있지 않은가? 어? 그럼 포인터변수에 대입할수 있겠네 ?? 라고...? 한번 직접 해보자

#include <iostream>
using namespace std;

int main()
{
    int num[] = {1,2,3};
    
    int* pointer = num;
    
    cout << num << endl;
    cout << pointer << endl;
    return 0;
}

출력 결과를 보면 동일한 주소값을 갖는 것을 볼 수 있다.

하지만 여기서 중요한 것은 ! 배열이 포인터로 변환이 되는 것이지 배열과 포인터가 동일하다고는 볼수는 없다는 것이다. 이것을 증명하려면

크기를 비교하는 sizeof함수를 적용 해보면 된다.

cout << sizeof(num) << endl;
cout << sizeof(pointer) << endl;

자 2개의 사이즈가 다르다. 즉 배열인 num은 배열의 크기를 나타내는거고 pointer는 배열중 1개의 변수의 크기만을 나타낸다. 그렇기 때문에 2개의 주소값이 같다고 하더라도 포인터 변수와 배열이 같다고 볼수가 없는 것이다.

 

자 여기서 한가지 더 실험을 해보자

이 코드는 컴파일이 될까?

int num[] = {1,2,3};
int* pointer = &num;

다음과 같은 컴파일 에러가 뜬다. & 키워드는 주소를 나타내는 연산자 인데 왜 컴파일이 안되는지 이해가 안될수도 있을 것이다. 이것이 무엇을 의미하냐면 배열도 하나의 타입이라는 뜻이다. 즉 nums의 타입은 int가 3개짜리인 배열의 포인터가 타입이 되는 것이다. 아래 문장만 해석해봐도 그런 의미인 것을 알 수 있다.

더 정확하게 확인하기 위해서 typeid 함수를 통해서 확인을 해보자.※ typeid 함수는 해당 변수가 어떤 타입인지를 반환해주는 함수이다.

cout << typeid(1).name() << endl;
cout << typeid(num).name() << endl;
cout << typeid(&num).name() << endl;
cout << typeid(&num[0]).name() << endl;

결과는 다음과 같다. 

&num은 PA3_i 인것을 볼수 있다 int 변수가 3개 짜리인 배열의 포인터 타입 이라는 의미이다.

반면에 &num[0]은 int 포인터 변수 인것을 볼수 있다. 

좀 다른 길로 빠졌는데...ㅎㅎ 여기서 중요한 것은 포인터 변수가 배열의 주소값 즉 배열의 첫번째 원소의 주소를 알수 있다는 것이다. 

그럼 포인터 변수로 첫번째, 두번째, 세번째 원소에 대해 접근도 가능하다는 것을 알 수 있다

역참조 * 키워드를 사용해서 한번 접근해보자.

cout << *pointer << endl;
cout << *(pointer + 1) << endl;
cout << *(pointer + 2) << endl;

다음과 같이 num 배열의 각 원소들에 잘 접근한 것을 볼 수 있다.

아까 위에서 잠시 나오긴 했지만 &num[0]이 int 포인터 변수 였던 것을 알수 있다. 이것을 그대로 *num, *(num+1) 이런식으로 접근하면 위의 결과와 동일한 것을 알수 있다. 

 

그럼 ++ 키워드를 사용해서 변수에 접근하는건 어떨까 

cout << *pointer << endl;
pointer++;
cout << *pointer << endl;

잘 컴파일이 되는 것을 볼 수 있다.

그럼 배열은 어떨까?

 

cout << *num << endl;
num++
cout << *num << endl;

배열은 컴파일 에러가 난다.

배열은 non modifire 형태라고 불리기도 하는데, 이처럼 자기 스스로 값을 바꿀 수가 없다.

 

이처럼 강의를 통해 공부한 내용들과 내가 추가로 궁금한 내용들에 대해서 정리하였다. 이렇게 포인터 상편에 대한 포스팅을 마치도록 하겠다.

 

 

[Referece]

패스트캠퍼스 C++실력완성 올인원패키지

반응형