# Address Value의 이해
데이터의 주소값이란 해당 데이터의 저장된 메모리의 시작 주소를 의미합니다. 여기서 궁금한 점이 있을 수 있다.
메모리는 물리 메모리를 이야기하는 것이 아닌 CPU가 바라보는 논리주소, 즉 가상 주소를 의미한다. 가상 메모리리 관련해서는 아래 포스팅을 첨부한다.
2024.01.15 - [Computer Science/Operation System] - [OS] Virtual Memory와 Paging기법
다시 설명하면, 논리주소의 값으로 저장된 메모리의 시작 주소를 의미하게된다. C++에서는 주소값을 1Byte 크기의 메모리 공간으로 나누어 이해하면된다. int형은 4Byte이지만 데이터 주소 값은 1Byte이다.
흠... 이게 사실 C계열의 언어의 진입장벽중 하나이다. 사실 이해하기가 어려운데 한번 적절한 예시를 찾아서 말해보겠다.
Example) 집 주소 - 집(메모리에 저장된 데이터): 집은 실제 물리적인 공간이다. 즉 데이터가 쌓이는 공간(logical space)을 나타낸다. - 집의 주소(포인터): 집의 주소는 집 위치를 알려준다. 이 주소를 통해 우리는 집을 찾을 수 있다. - 주소를 통해 집 접근하기(포인터 사용): 누군가 집 주소를 알려주면 집을 찾아 갈 수있다. |
위의 내용이 더 이해하기 쉬울 수도 있다. 위 예시에서 빠진건 저장된 메모리의 시작 주소를 의미한다는것 은 꼭 기억해야한다. 또한, JAVA는 자체적으로 메모리를 관리해주는 managed 언어이기 때문에 포인터를 지원하지 않는다.
# Pointer의 이해
포인터도 변수이다. 단 정수형, 문자형이 아닌 메모리의 주소값을 저장하게 되는 변수를 의미한다.
int 변수형 n은 4bytes를 차지한다. 그리고 시작주소는 0x12를가리키고 0x12는 주소값이 된다. 이를 저장하기 위해서는 포인터 변수 ptr을 선언하여 0x5E에 저장을 하게된다. 동작이 이루어지는 것은 어렵지 않다. 손으로 조금만 그려보면 이해를 할 수 있다.
# Pointer의 연산자
#include <iostream>
using namespace std;
int main(void){
int n = 100; // 변수의 선언
int* ptr = &n; // 포인터의 선언
cout << n << endl;
cout << ptr << endl;
char c = 'k'; //변수의 선언
char* ptrC = &c; // 포인터의 선언
cout << c << endl;
cout << ptrC << endl;
return 0;
}
//출력//
/*
100
0x7ffdae078534
k
kd
*/
pointer에는 두가지 연산자가 있다.
- 주소 연산자 (&)
- 변수 이름 앞에 사용 EX) int* ptr = &n;
- 변수의 주소값을 반환 EX) n의 주소값 : 0x7fff2b9bf444
- 앰퍼센트라라고 부르고 번지 연산자라고 부름
- 참조 연산자 (*)
- 포인터의 이름이나 주소 앞에 사용 EX) int* ptr = &n;
- 포인터 주소에 저장되어있는 값을 반환 EX) ptr : 0x7fff2b9bf444
- 에스크리터라고 부름
선언하는 방식은 위아래와 같다.
타입* 포인터이름;
char* cPtr;
int* ptr;
double* dPtr;
변수명 앞에 붙이거나 타입 바로 뒤에 붙이는데 Convention을 보니 변수 바로 뒤에 붙이는것 같다. 그리고 항상 C++에서 유의할 점이 있는데 모든 변수의 타입은 사용하기 전에 항상 init을 해줘야한다는 것이다.
포인터의 참조 개념도 있는데 포인터를 선언하고 그 포인터를 다시 포인터로 선언하는 구조이다. 우리는 이를 다중 포인터 구조라고 부르고 해당 개념은 Dimension에 따라서 달라진다. 2중은 2차원에서 3중은 3차원에서 사용되곤한다.
#include <iostream>
using namespace std;
int main(void){
int n = 100; // 변수의 선언
int* ptr = &n; // 포인터의 선언
int** pptr = &ptr; //포인터의 참조 선언
int*** ppptr = &pptr; //포인터 참조의 참조 선언
int**** pppptr = &ppptr; //포인터 참조의 참조의 참조 선언
cout << n << endl;
cout << ptr << endl;
cout << pptr << endl;
cout << ppptr << endl;
cout << pppptr << endl;
return 0;
}
//출력
/*
100
0x7fff881ea1b4
0x7fff881ea1b8
0x7fff881ea1c0
0x7fff881ea1c8
*/
# Call by Value , Call by Reference
기술면접에도 종종 물어보는 질문중 하나이다. 값에 의한 호출, 참조에 의한 호출이라는 의미인다. 포인터의 연장선에 서있기 때문에 좋을거 같아서 들고 왔다. 아래는 두가지다 값을 교환하는 예제이다.
#include <iostream>
using namespace std;
void Swap(int a , int b){
int temp = a;
a = b;
b = temp;
cout << "Call By Value" << endl;
}
void SwapRef(int* a , int* b){
int temp = *a;
*a = *b;
*b = temp;
}
int main(void){
int a = 2;
int b = 3;
Swap(a, b);
cout << a << endl;
cout << b << endl;
cout << "Call By Reference" << endl;
SwapRef(&a, &b);
cout << a << endl;
cout << b << endl;
cout << " " << endl;
return 0;
}
Call By Value
2
3
Call By Reference
3
2
코드를 보면 Call By Value를 했을때는 값이 변하지 않음을 알수 있다. 이는 우리가 함수호출로 하였고 함수호출로 선언된 지역변수들만 값이 변한것이다. 뭐 사실 다시 return해서 교체를 해주면된다. 하지만 교체하는 비용도 소모가 된다는 점을 알고 있어야한다.
Call By Reference는 값이 변경되었는데 이는 참조연산자를 역참조하여 메모리에 저장된 값에 직접 접근하여 그 값을 swap해주었기때문에 값이 변경될 수 있었다.
아래 그림은 이해를 돕기위한 아주 적절한 예시이다.
# 배운 점
뭐 사실 코드를 직접 작성하면서 어떤 식으로 활용해야할지, 예제 코드도 많이 봐야하지만 일단, 아예 포인터를 모르고 있던 상황은 아니였다. 정처기에서도 굉장히 많은 프로그래밍 문제에 나오고 포인터,배열을 섞어서 시험 문제를 출제하곤한다. 사실 머리속으로 푸는건 무리가 있고 이론적으로 알고 있다면, 손으로 그리면서 풀어내는게 제일 베스트이다.
'Language > C++' 카테고리의 다른 글
[C++] Class & 접근제어지시어 (0) | 2024.01.22 |
---|---|
[C++] Structure Type(구조체) (0) | 2024.01.19 |
[C++] Assignment 주소록(1) - 요구사항 분석 (0) | 2024.01.13 |
[C++] C++ 동작 원리 (0) | 2024.01.13 |
[C++] C++ 배우기를 선택한 이유 (0) | 2024.01.08 |