포인터
동적 메모리에서 할당하는 메모리를 주소로 참조해야 할 때 사용하는 것
int* p;
double *p;
char *p;
short *p;
> 포인터는 생성된 직후에 초기화되어있지 않아, 사용하기 전에 반드시 초기화를 해 주어야 한다.
주소 연산자
p=&number;
int price=86600;
int *p;
p=&price;
- 변수정의
- 포인터 정의
- 변수의 주소에 포인터 저장
간접 참조 연산자
포인터는 포인터가 가리키는 메모리 공간의 값을 읽어오거나 변경할 수 있기 때문에 유용하다. 그리고 내용을 가져오려면 *p라고 하면 된다. 이것을 간접참조라고 한다.
int price=86600;
int *p=&price;
> 포인터가 아무것도 가리키고 있지 않을 때는 nullptr로 설정하는 것이 좋다. nullptr을 사용하면 어디선가 접근을 시도하면 시스템에서 오류를 감지하고 이것을 자동으로 해결해 주기 때문이다.
주의점 > 포인터 타입과 변수의 타입은 일치해야 한다.
#include <iostream>
using namespace std;
int main(){
int number=10;
//변수 number의 주소를 계산해서 p에 대입
//p가 가리키는 공간에 저장된 값을 출력
cout<<*p<<endl;
return 0;
}
nullptr
만약 포인터가 선언만 되고 초기화되지 않았다면 포인터는 임의의 주소를 가리키게 된다. 이 상태에서 포인터를 이용하여 메모리의 내용을 변경하면 문제가 발생한다.
//ex
int *p;
*p=100;
//따라서
int *p=nullptr;
> nullptr을 사용하여서 어딘가에 접근하려고 시도하면 시스템에서 자동적으로 오류를 감지하고 해결하기 때문이다.
#include <iostream>
using namespace std;
void f(int i){
cout<<"f(int)"<<endl;
}
void f(char *p){
cout<<"f(char *)"<<endl;
}
int main(){
f(NULL);
return 0;
}
/*main()문 ->*/
int main(){
f(nullptr);
return 0;
)
동적 메모리 할당
- 프로그램이 실행도중에 동적으로 메모리를 할당받는 것
> 아직 사용하지 않은 메모리공간을 관리하는 것을 히프라고 한다. 동적메모리는 히프에서 할당받는 메모리이다
> 프로그램은 메모리를 얼마나 할당 받을 것인지를 결정하고 라이브러리 함수를 호출하여 운영체제에게 메모리를 요청하는 단계가 필요하다. 만약 충분한 메모리가 존재하면 그 요청은 승인되고 메모리가 할당된다.
> 프로그램은 할당된 메모리를 사용하여 작업을 하고, 사용이 끝나면 메모리를 다시 운영체제에 반납한다. 만약 메모리를 반납하지 않으면 다른 프로그램이 동적메모리를 사용할 수 없다. 그래서 반드시 동적 메모리는 명시적으로 반납을 해주어야 한다.
/*new 연산자*/
p=new T;//동적 메모리 타입
p=new T[N];//동적 메모리 개수
p=new T[n] {initializer1,....,initializerN};//동적메모리 초기값
->
int *p;
p=new int[5];
/*특정한 값으로 초기화 방법*/
int *p=new int[5] {1,2,3,4,5};
/*delete 연산자*/
delete p;
delete [] p;
> 할당받은 공간은 해제할 때 반드시 delete [] p;를 사용해야 한다.
#include <iostream>
#include <time.h>
using namespace std;
int main() {
int* ptr;
srand(time(NULL));
ptr = new int[10];
for (int i = 0; i < 10; i++)
ptr[i] = rand();
for (int i = 0; i < 10; i++)
cout << ptr[i] << "";
delete[]ptr;
cout << endl;
return 0;
}
스마트 포인터
/*정적 변수를 가리키는 포인터*/
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> p(new int);
//nuique_ptr: 포인터의 자료형
//new int: 포인터의 초기값
*p = 99;
//p를 사용하며, p가 삭제되면 동적메모리도 같이 삭제
//삭제 이유: 메모리 누수 발생을 막기 위해
}
/*정수형 배열을 동적으로 생성하여 스마트 포인터로 가리킴*/
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int[]>buf(new int[10]);
for (int i = 0; i < 10; i++) {
buf[i] = i;
}
for (int i = 0; i < 10; i++) {
cout << buf[i] << "";
}
cout << endl;
return 0;
}
- 다른 스마트 포인터 종류
- *unique_ptr
- *shared_ptr
const
//1
const int *p1;
//2
int *const p2;
//3
const int * const p3;
- p1은 변경되지 않는 정수를 가리키는 포인터이다. 이 포인터를 통하여 참조되는 값은 변경이 불가능하다
- p2는 정수에 대한 상수 포인터이다. 정수는 변경될 수 있지만 p2는 다른 것을 가리킬 수 없다. 상수 포인터는 재할당 될 수 없다.
- p3는 상수에 대한 상수 포인터이다. 포인터가 가리키는 값도 변경이 불가능하고 포인터 p3도 다른 것을 가리키게끔 변경될 수 없다.
> 멤버 함수를 const로 정의하면 함수 안에서 멤버 변수를 변경하는 것은 금지된다. const 객체를 가리키는 포인터를 정의하면 이 포인터로 호출할 수 있는 함수는 const 뿐이다.
#include <iostream>
using namespace std;
class Circle {
private:
int radius;
public:
Circle() :radius(10){}
~Circle() {}
void setRadius(int radius) { this->radius = radius; }
int getRadius()const { return radius; }
};
int main() {
Circle* p = new Circle();
const Circle* pConstObj = new Circle();
Circle* const pConstPtr = new Circle();
cout << "pRect->radius:" << p->getRadius() << endl;
cout << "pConstObj->radius: "pConstObj->getRadius() << endl;
cout << "pConstPtr->radius: "pConstPtr->getRadius() << endl << endl;
p->setRadius(30);
pConstPtr->setRadius(30);
cout << "pRect->radius:" << p->getRadius() << endl;
cout << "pConstObj->radius: "pConstObj->getRadius() << endl;
cout << "pConstPtr->radius: "pConstPtr->getRadius() << endl;
return 0;
}