참조자와 포인터
참조자... 포인터... 학교다니면서도 엄청나게 애먹었던 내용들이었고 제대로 습득한 기억도 없다😅 꼭 잘 알고 있어야 한다며 엄청나게 강조하시던 교수님의 말씀을 한귀로 듣고 한귀로 흘린 내 잘못이다. 회사에 오고 나서 다시 만날 것이라 생각은 했지만 이렇게나 중요한 존재일 줄이야^^ 선임님(지금은 책임님)께서 오전동안 거의 두시간에 걸쳐 참조자, 포인터, 가상함수에 대해서 알려주셨다. 이해하기 쉽게 알려주셔서 뭐가 뭔지 알아들었고 정리하면서 다시 내용을 상기하고자 한다!
+ 책 열혈 C++ 내용과 게시글 하단의 링크도 참고하여 작성
참조자
참조자라는 것은 성격상 포인터와 비유되기 쉬운데 포인터 몰라도 이해할 수 있는 개념이니 포인터의 개념까지 끌어들여서 스스로를 괴롭히지 말자!
우선 변수에 대해 이야기해보자. 변수는 할당된 메모리 공간에 붙여진 이름이다. 그리고 그 이름을 통해서 해당 메모리 공간에 접근이 가능하다.
아래 소스는 int형 크기만큼 메모리공간을 할당받아 그 공간에 abc라는 이름(변수명)을 붙이고 10이라는 값을 넣은 것이다.
int abc = 10;
만약 이 공간을 abc 말고 name이라는 다른 이름으로 부르고 싶다고 하면 아래와 같이 작성하면 된다. 여기서 name의 역할이 참조자인 것이다. 단순히 생각해서 참조자는 별명같은 존재이다. 그러니 어렵게 생각하지말자
int &name = abc;
이제 abc공간에 있는 10이라는 값에 name도 이제 접근할 수 있다. int abc라고 적어서 abc공간이 생겼다면 int &name이라고 적었으니 name공간도 생긴것인가 싶지만 실제로는 생기지 않는다. name은 그저 abc공간을 부르는 또다른 이름이기 때문에 공간이 생기지 않는 것이다. 아래 그림 참고하자
abc와 name은 같은 공간을 의미하는 변수명이기 때문에 앞으로는 abc의 데이터를 확인해도 name의 데이터를 확인해도 10이 나올 것이다.
만약 name의 값을 3000으로 바꿨다면 abc의 값에 변화가 있을까? 변화가 당연히 있다! name은 abc의 또 다른 이름일뿐 서로 다른 데이터를 칭하는 것이 아니기 때문에 name의 값을 3000으로 바꾸면 당연히 abc의 값도 3000으로 바뀐다. 반대로 abc의 값을 바꾸면 name의 동일하게 값이 변한다.
다음은 참조자의 특징 3가지이다. (이외에도 더 있지 않을까?)
특징1. 참조자의 수에는 제한이 없다.
int abc = 10;
int &name = abc;
int &name2 = abc;
int &name3 = abc;
int &name4 = abc;
.
.
.
위와 같이 참조자의 수에는 제한이 없기 때문에 abc공간을 name, name2, name3, name4로도 부를 수 있는 것이다. (근데 참조자가 많으면 헷갈리기만 하고 쓸모가 없지 않을까?)
특징2. 참조자를 대상으로도 참조자를 선언할 수 있다.
int abc = 10;
int &name = abc;
int &name1 = name
int &name2 = name1;
int &name3 = name2;
참조자를 대상으로도 참조자를 선언할 수 있는데 이것도 의미는 특징1과 동일하게 abc공간을 name, name2, name3로 부를 수 있다. (참조자를 대상으로 또 다른 참조자를 만드는 일이 흔히 필요하지는 않다고한다.)
특징3. 선언과 동시에 누군가를 참조해야만 한다.
int &name; // X
int &name = 10; // X
int &name = NULL; // X
참조자는 변수에 대해서만 선언이 가능하고, 선언됨과 동시에 누군가를 참조해야만 한다. 그래서 위와 같이 작성은 불가능하다. 참조자는 변수에 또 다른 이름을 붙이는 것이기 때문에 (1)참조자만 선언해두는 것이나 (2)데이터를 집어넣는 것, (3)NULL로 초기화하는 것은 불가능하다.
포인터
변수는 할당받은 메모리 공간에 데이터가 들어갔지만 포인터는 값을 저장하는게 아니라 메모리 주소를 저장한다.
int *ptr;
int* ptr2, ptr3;
int *ptr4, *ptr5;
위와 같이 포인터를 선언하면 되는데 두 번째 줄과 세 번째 줄의 차이가 보이는가? 두 번째 줄은 int에 포인터를 붙여주었고 세 번째 줄은 각각 변수명 앞에 포인터(*)를 붙여주었다. 두 번째 줄처럼 변수를 선언하게 되면 ptr2는 포인터변수가 되지만 ptr3은 단순한 int형 변수가 된다. 그렇기 때문에 여러 포인트 변수를 선언하는 경우에는 포인터가 각 변수마다 작성해줘야 한다.
포인터 변수는 일반 변수와 동일하게 선언 시 초기화 되지 않는다. 초기화 하기 위해서 주소값을 저장해주어야 하는데 변수의 주소를 얻으려면 아래와 같은 형태로 앰퍼샌드 & 를 사용해야한다.
int abc = 10;
int *ptr = &abc;
이렇게 되면 데이터를 저장하는 방 abc에는 10이 저장되고, 주소를 저장하는 방 ptr에는 abc의 주소가 저장된다.
아래 소스와 출력결과화면을 보자.
#include <iostream>
using namespace std;
int main()
{
int abc = 10;
int *ptr;
ptr = &abc;
cout<<"abc = "<<abc<<endl;
cout<<"ptr = "<<ptr<<endl;
cout<<"*ptr = "<<*ptr<<endl;
cout<<"abc 주소 = "<<&abc<<endl;
cout<<"ptr 주소 = "<<&ptr<<endl;
}
abc에는 10이 저장되었고, ptr에는 abc의 주소가 저장된 것을 확인할 수 있다. ptr에 앰퍼샌드를 붙이면 abc의 데이터인 10이 출력되는 것도 확인할 수 있었다. 잘 이해가 안간다면 아래 그림을 참고해보자
다음 소스처럼 배열에서도 포인터를 사용할 수 있다.
#include <iostream>
using namespace std;
int main()
{
int arr[10];
for(int i=0;i<10;i++){
arr[i] = i;
cout<<arr[i]<<" ";
}
cout<<endl<<endl;
cout<<arr<<endl;
cout<<arr+1<<endl;
cout<<arr+2<<endl;
}
10칸짜리 배열을 만들어주고 arr를 출력해보니 배열의 첫 번째 주소가 출력됐다. 그 다음 arr+1은 배열의 두 번째 주소, arr+2는 배열의 세 번째 주소가 출력되었다.(데이터형이 int형이어서 4바이트씩 차이가 나는 것이다.) 이처럼 배열에서도 포인터를 사용하여 구현이 가능하고, 포인터는 배열을 반복할 때 사용할 수 있다.
말로 어떻게 설명해야할지 감이 안잡혀서 아래 링크를 참고하면서 적었는데 정리가 너무 잘 되어 있어서 첨부한다.
https://boycoding.tistory.com/199
C++ 07.07 - 포인터 소개 (Introduction to pointer)
07.07 - 포인터 소개 (Introduction to pointer) '01.02 - 변수, 초기화 및 할당' 포스트에서 변수는 값을 보유하고 있는 메모리 조각의 이름이라는 것을 배웠다. 프로그램이 변수를 인스턴스화 할때 사용 가
boycoding.tistory.com