728x90
황기태 저자의 명품 C++ Programming 개정판을 읽고 학습한 내용을 정리한 포스트입니다!
https://search.shopping.naver.com/book/catalog/32436115747
포인터
- C/C++ 언어에서 포인터(pointer)는 실행 중 메모리의 주소 값이다.
- 주소(포인터)를 이용하여 메모리에 직접 값을 쓰거나 메모리로부터 값을 읽어올 수 있다.
변수의 메모리 주소
- 변수란 프로그램 내에서 사용하는 이름
- 각 변수마다 메모리 공간이 할당 된다.
int n;
n = 3;
- 위 코드에서 n은 정수를 저장할 메모리 공간에 대한 이름
- 그러나 값 3이 메모리 몇 번지에 기록되는지는 알 수 없는데 => 프로그램이 실행을 시작할 때, 비로소 변수 n의절대 메모리 주소가 정해지기 떄문
포인터 변수 선언
- c/c++에서 포인터 변수란 포인터 즉 주소를 저장하는 변수
- c++에서는 실행 중에 변수의 메모리 주소를 알아 낼 수 있고, 이 주소를 포인터 변수에 저장 가능
- 다음은 *연산자를 사용하여 포인터 변수를 선언하는 사례이다.
int *p; // 정수를 저장하는 메모리에 대한 포인터 변수 p 선언
p = &n // p에 n의 주소를 저장
- 변수 n의 주소를 실행 or 컴퓨터마다 다를 수 있기에, & 연산자를 이용하여 실행 중에주소 값을 알아낸다.
- 포인터 변수는 다음과 같이 타입이 일치되지 않으면 오류가 발생한다.
int *p = &n; // 옳은 코드
int *t = &d; // 잘못된 코드. 변수 d는 실수(double)을 저장하는 변수이므로 오류
포인터 변수 활용
*p = 25; // n에 25가 저장됨
*q = 'A'; // c에 문자 'A'가 저장됨
*r = 3.14; // d에 3.14가 저장됨
- *p는 'p가 가리키는 곳' 혹은 '값'을 뜻하는 표기
- *p = 25;는 포인터 p가 가리키는 곳에 25를 저장하는 코드
배열과 포인터
- C/C++에서 배열 이름은 배열 메모리의 시작 주소로 다룬다.
int n[10];
- 다음 코드는 같은 것으로 배열 n의 시작 주소에서 5번째 떨어진 위치에 8을 저장하는 것
n[5] = 8;
*(n+5) = 8;
- 따라서, 다음과 같이 포인터 변수 p에 배열 n의 시작 주소를 줄 수 있다.
int *p;
p = n; // p = &n[0];과 동일
*p = 100; // n[0] = 100;과 동일
*(p+5) = 8 // n[5] = 8;과 동일
// 아래와 같이 산술연산도 가능
p = p + 7; // p는 n[7]의 주소를 가지게 됨
*p = 99; // n[7] = 99;와 동일
- 그러나 배열 이름은 상수 포인터이므로 다음과 같이 할 수 없다.
n = n+1; // 오류. 배열 이름은 상수이므로 변경 불가
포인터를 매개 변수로 가진 함수 만들기
#include <iostream>
using namespace std;
bool equal(int* p, int* q); // 함수의 원형 선언
int main() {
int a = 6, b = 6;
if (equal(&a, &b)) cout << "equal" << "\n";
else cout << "not equal" << "\n";
return 0;
}
bool equal(int* p, int* q) {
// 포인터 매개변수
if (*p == *q) return true;
else return false;
}
- 포인터로 정수 2개를 전달받아 같은지 비교하는 equal 함수를 작성하였다.
- 함수의 매개 변수로 포인터를 선언한 후, 배열을 비교하는 함수를 작성하였다.
#include <iostream>
using namespace std;
bool equalArray(int* p, int* q, int size); // 함수의 원형 선언
int main() {
int a[] = { 1,2,3,4,5 };
int b[] = { 1,2,3,4,5 };
if (equalArray(a, b, 5))
cout << "arrays equal" << "\n";
else
cout << "arrays not equal" << "\n";
}
bool equalArray(int* p, int* q, int size) {
int i;
for (i = 0; i < size; i++) {
if (*p != *q)
return false;
p++; // p는 배열 a의 다음 원소를 가리킴
q++; // q도 배열 b의 다음 원소를 가리킴
}
return true;
}
- 또한, 포인터는 배열로 생각할 수 있으므로 equalArray()는 아래와 같이 작성 가능
bool equalArray(int* p, int* q, int size) {
int i;
for (i = 0; i < size; i++) {
if (p[i] != q[i])
return false;
}
return true;
}
&(앰퍼샌드) = 참조(reference) 변수
- 참조 변수는 한 객체가 다른 객체를 연결하는 수단.
- 즉, 다른 객체나 값에 대한 별명으로 사용한다고 생각하면 된다. => 크기가 큰 객체를 함수에 인수로 전달할 때 주로 사용
참조 변수 선언
- 문법 = 자료형& 참조 변수명 = 변수명;
- &는 포인터에서 사용하던 변수의 주소값을 저장할 때 사용하던 주소 연산자로 쓰인 것이 아니라! 타입을 식별하기 위한 식별자로 사용
- 즉, 주소가 아닌 참조이기 때문에 저장하는 변수와 같은 메모리를 참조
int num = 10;
int& refer = num;
- 참조 변수를 사용하면 대상 객체와 동일한 곳을 참조하는 별명과도 같은 역할이기 때문에 참조 변수는 참조 대상과 동일한 값을 가진다.
- 즉, 같은 주소를 가지기에 일반 변수의 값이 변경되면 참조 변수의 값도 변경된다.
주의 사항
- 참조 변수는 선언과 동시에 초기화하여야 한다.
- null 값을 저장할 수 있는 포인터와 다르게 null 을 참조 x
- 참조하려는 변수와 데이터 타입을 일치켜야 한다.또한 const로 선언된 변수는 const로, non-const는 non-const로만 참조할 수 있다.
- 한번 참조한 변수는 재참조 불가능
함수의 매개 변수로 활용
- 참조 변수를 매개 변수로 사용하면 인수의 별명으로 이용
- 동일한 메모리를 참조하기 때문에 값의 복사가 이루어지지 않아 크기가 큰 객체를 함수의 인수로 전달할 때 사용하면 복사하는데 드는 비용을 줄여 성능을 향상할 수 있어 자주 사용
- 또한 포인터를 매개변수로 사용하여 역참조를 통해 원본 데이터를 수정할 수 있는 것처럼, 참조 변수를 매개 변수로 사용하면 값의 복사가 아닌 인수로 전달된 원본 데이터와 동일한 메모리를 참조하므로 원본데이터 수정이 가능
#include <iostream>
using namespace std;
void SwapVal(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 10;
int y = 20;
cout << "x: " << x << "y: " << y << endl;
SwapVal(x, y);
cout << "x: " << x << "y : " << y << endl;
return 0;
}
- 두 값을 교환하는 함수에 참조 변수를 매개 변수로 사용하여 제작
- 인수에 x, y가 전달되면 매개변수 a, b가 x, y에 대한 참조로 선언되어 직접 x,y의 값의 교환이 가능해진다.
- 함수 매개변수에 구조체와 같은 사용자 정의형을 사용한다면 참조를 통해 쉽게 접근 가능하다.
#include <iostream>
#include <string>
using namespace std;
struct Human {
string name;
int age;
double tall;
};
void ChangeStruct(Human& h, string n, int a, double t) {
h.name = n;
h.age = a;
h.tall = t;
}
int main() {
Human human1 = { "김00", 26, 175.8 };
cout << human1.name << " " << human1.age << " " << human1.tall << endl;
ChangeStruct(human1, "박00", 100, 1651.);
cout << human1.name << " " << human1.age << " " << human1.tall << endl;
return 0;
}
포인터와 참조 변수
- 참조형 변수는 접근할 때 역참조 포인터와 같은 역할
- 내부적으로도 참조 변수를 사용할 때 컴파일러는 포인터를 이용하여 접근
- 따라서 => *ptr과 ref는 동일하게 취급된다. 또한 참조형 변수는 선언과 동시에 초기화되어야 하고 null로 초기화하거나 참조를 변경할 수 없으므로 포인터보다 안전하게 취급받는다.
- int* ptr = &num
- 위와 같이 포인터는 포인터 변수 ptr이 stack에 4바이트 공간으로 존재, 변수 num의 주소를 ptr이 가지고 있는 것
- 둘은 서로 다른 변수
int n = 5;
int *p = &n;
/*
이 때 &n == 주소 == p
*p == 5
&p == p의 주소
*/
728x90
'Programming Language > C++' 카테고리의 다른 글
[C++] 동적 메모리 할당 및 반환 (1) | 2023.11.30 |
---|---|
[C++] 객체 포인터와 객체 배열 (0) | 2023.11.30 |
[C++] 구조체 (0) | 2023.11.29 |
[C++] priority queue with Cpp (0) | 2023.11.29 |
[C++] 접근 지정자와 인라인 함수 (0) | 2023.11.29 |