11.1 포인터를 쓰는 이유
포인터를 사용하면 static mermory allocation인 배열의 단점을 극복할 수 있습니다. 포인터는 실행 즉시 정수에 대한 '메모리'를 할당하고 삭제할 수 있기 때문에 요소가 추가 됨에 따라 배열의 크기를 변경할 수 있습니다.
11.2 포인터 사용법
1) 포인터 변수 선언
포인터 변수는 메모리 주소를 저장합니다.
선언하려면 int *pcount;
값을 할당하려면 pcount =&count; //count의 주소를 할당한다.
pcount++;형식으로 주소를 한 칸 늘릴 수 있습니다. 배열의 이름이 주소였던 것과 유사합니다.
선언과 동시에 초기화하려고 *pcount =&count; 형식으로 사용하면 오류가 납니다.
2) 역참조 연산자
포인터 변수를 사용할 때, *(역참조 연산자;dereference operator,간접 연산자;indirection operator)를 사용하면 실제 값을 출력할 수도 있습니다. 이렇게 포인터를 통한 값 참조를 간접 참조(indirection)이라고 합니다.
(*pcount)++를 간접 참조라고 하고 count++을 직접 참조라고 합니다.
#include <iostream>
using namespace std;
int main()
{
int count = 5;
int* pcount = &count; //선언할때 *하나 붙으면 주소저장
cout << "The value of count is " << count << endl;
cout << "The address of count is " << &count << endl;
cout << "The address of count is " << pcount << endl;
cout << "The value of count is " << *pcount << endl; //사용할 때 붙은 *는값을 가져오는 역참조 연산자
return 0;
}
다음과 같이 *은 포인터선언과 역참조 연산자(그리고 곱셈에도)에 쓰이기 때문에 많이 헷갈립니다. 역참조 연산은 포인터변수를 '사용'할 때 주로 쓴다는 것을 기억하면 편합니다. 이를 유의하며 공부합시다.
3) 포인터 사용시 주의점
포인터 변수는 여러 타입에서 사용될 수 있지만 반드시 같은 유형의 변수 주소를 할당해야 합니다.
int area =1;
double* pArea =&area;로 사용 할 수 없습니다.(구문 오류)
또, 포인터 변수가 아닌 변수에 주소를 할당할 수는 있지만 포인터변수를 일반 변수에 할당할 수는 없습니다. 같은 유형인 포인터에 할당하는 것은 가능합니다.
int area =1;
int* pArea =&area;
int i = pArea; //구문 오류
포인터는 반드시 초기화해놓고 사용해야 합니다. 하지 않으면 runtime 오류가 발생하거나 실수로 중요한 데이터가 변경될 수 있습니다. c++ 라이브러리는 대체로 NULL로 초기화하는 것을 추천합니다.
int* p1, p2이면 int* p1과 int p2를 선언한 것입니다. 따로 선언하는 것을 추천합니다.
checkpoint)
11.1) 포인터 변수는 어떻게 선언하는가? 지역 포인터 변수는 기본 값이 있는가?
int *pointer;이런 형식으로 선언한다.
지역 포인터를 초기화하지 않으면 임의의 값이 저장된다. 미리 NULL로 초기화하는 것을 추천한다.
11.2) 변수의 주소를 포인터 변수에 어떻게 대입하는가? 다음 코드는 무엇이 잘못되었는가?
int x =30;
int* pX = x;
cout <<"x is "<< x << endl;
cout <<"x is "<< pX << endl;
& 주소 연산자를 사용해 주소를 포인터 변수에 저장한다.
int*형을 int로 초기화하려고 했다. 주소값을 저장해야 한다.
수정 코드 ▼
#include <iostream>
using namespace std;
int main()
{
int x = 30;
int* pX = &x;
cout << "x is " << x << endl;
cout << "x is " << pX << endl;
return 0;
}
11.3) 다음 코드의 출력을 무엇인가?
#include <iostream>
using namespace std;
int main()
{
int x = 30;
int* p = &x;
cout << *p << endl;
int y = 40;
p = &y;
cout << *p << endl;
return 0;
}
30
40
11.4) 다음 코드의 출력을 무엇인가?
#include <iostream>
using namespace std;
int main()
{
double x = 3.5;
double* p1 = &x;
double y = 4.5;
double* p2 = &y;
cout << *p1 + *p2 << endl;
return 0;
}
8
11.5) 다음 코드의 출력은 무엇인가?
#include <iostream>
using namespace std;
int main()
{
string s = "ABCD";
string* p = &s;
cout << p << endl;
cout << *p << endl;
cout << (*p)[0] << endl;
return 0;
}
000000C34194FA58 (s의 주소)
ABCD
A
11.6) 다음 코드는 무엇이 잘못되었는가?
double x = 3.0;
int* pX = &x;
double*에 int*을 대입할 수 없다.
아래는 알맞게 고친 코드다.
double x = 3.0;
double* pX = &x;
11.7) 만일 p1과 p2가 다음과 같이 정의된 경우 변수 p1, p2 둘 다 포인터인가?
double* p1,p2;
아니다. p1은 double포인터 이지만 p2는 double 자료형의 값이다.
11.3) typedef 키워드를 사용한 동의어 유형 정의
구조체를 공부해 봤다면 typedef 키워드에 대해 알고 있을 것입니다.
typedef int integer;
integer a = 1;
typedef int* intPointer;
intPointer p; //int* p;와 동일한 선언
typedef는 다음과 같이 자료형의 다른 이름을 정해줄 수 있는 키워드입니다.
checkpoint
11.8) double*에 대한 동의어로 doublePointer라는 이름의 새로운 유형을 어떻게 정의하는 가?
typedef double* doublePointer;
11.4) 포인터와 const의 사용
포인터가 가리키는 '값'을 상수로 만들려면 다음과 같이 사용하고
double radius=20;
const double* p=&radius;
포인터가 가리키는 '주소'를 상수로 만들려면 다음과 같이 사용합니다.
double radius=20;
double* const p=&radius;
이때, 반드시 하나의 statement에서 선언과 동시에 초기화 되어야 합니다.
상수 포인터는 값은 바뀔 수 있지만 포인터가 가리키는 주소는 바꿀수 없습니다.
double radius =5;
double* const p = &radius;
*p = 6; //ok
double length = 5;
p=&length; // p는 상수포인터이기 때문에 잘못된 구문
const double* k =&radius;
*k = 6; // 상수 데이터를 가리키기 때문에 잘못된 구문
k =&length; //ok
const double* const l = &radius;
*k =6; //잘못됨
k =&length; //잘못됨
checkpoint)
11.9) 다음 코드에서 무엇이 잘못되었는가?
int x;
int* const p = &x;
int y;
p =&y;
p = &y불가
11.10) 다음 코드에서 무엇이 잘못되었는가?
int x;
constint* p = &x;
int y;
p =&y;
*p=5;
*p=5; 불가
11.5) 배열과 포인터
배열의 이름은 배열의 시작주소를 나타냅니다. 배열은 근본적으로 포인터이기 때문입니다.
또, 배열의 첫번째 '값'에 접근하기 위해 포인터를 사용할때는 *list, 두번째 주소부터는 *(list+1),*(list+2)...이런 식으로 사용합니다. 왜냐하면 int형 list의 시작주소가 1000이라면 list +1은 자동으로 1000+sizeof(int)가 되기 때문입니다.
*(list+1)은 list[1]이 나오지만 *연산자가 +보다 우선순위가 높기 때문에 *list+1을 하면 (list[0]+1)값이 나옵니다.
배열과 포인터는 서로 밀접한 관계를 가지고 있습니다. 배열의 인덱스를 사용하는 방법과 배열의 포인터를 이용하는 방법 모두 배열의 값에 접근할 수 있습니다.
#include <iostream>
using namespace std;
int main()
{
int list[6] = { 11,12,13,14,15,16 };
int* p = list;
for (int i = 0; i < 6; i++) {
cout << "address: " << (list + i) << " value: "
<< *(list + i) << " "
<< " value: " << list[i] << " "
<< " value: " << *(p+i) << " "
<< " value: " << p[i]
<< endl;
}
return 0;
}
address: 00000009A4AFF748 value: 11 value: 11 value: 11 value: 11 address: 00000009A4AFF74C value: 12 value: 12 value: 12 value: 12 address: 00000009A4AFF750 value: 13 value: 13 value: 13 value: 13 address: 00000009A4AFF754 value: 14 value: 14 value: 14 value: 14 address: 00000009A4AFF758 value: 15 value: 15 value: 15 value: 15 address: 00000009A4AFF75C value: 16 value: 16 value: 16 value: 16 |
하지만 배열과 포인터에는 한 가지 다른 점이 있습니다. 배열이 한번 선언되면 배열은 주소를 바꿀 수 없습니다.
int list1[10], list2[10];
list1= list2; //잘못된 구문
또, c에서는 문자열을 사용하기 위해 문자배열이나 문자 포인터, 두가지 방법을 사용할 수 있습니다. 아래의 예제는 각각 D,A,l,l,a,s,\0을 포함하는 문자열을 생성합니다. c++에서 문자열은 상수 포인터로 다뤄지기 때문에 문자 포인터를 사용해 문자열을 초기화할 수 없습니다.
char city[7] ="DAllas";
//char* pCity ="DAllas"; //c++에서는 안됨, c에서만 된다.
cout << city[1] << endl;
cout <<*(city+1) << endl;
//cout <<pCity[1] << endl;
//cout <<*(pCity+1) << endl;
checkpoint)
11.11) int* p를 선언하고 p의 현재 값은 100이라 하자. p+1은 무엇인가?
104 (int가 4byte라 할때)
11.12) int* p를 선언한 경우 p++,*p++,(*p)++의 차이점은 무엇인가?
int k = 100; int* p = &k;이고 &k가 0000003FFB95F7B4일때
p+1은 &k를 바꾸지 않고, &k+4 (주소)를 출력합니다. p++도 k의 주소값을 변경하지 않지만 후위 연산이기 때문에 출력되지 않습니다. 그냥 0000003FFB95F7B4를 출력합니다.
++연산이 포인터 연산보다 우선되기 때문에 *p++는 k값을 가져오고 p를 증가시킵니다. k의 실제 주소와 k의 값은 변하지 않습니다. 100이 출력되고 p는 000000B72579FB28가 되었습니다.
(*p)++는 k값을 변화시킵니다. k의 주소와 p는 변하지 않습니다.
11.13) 다음 코드의 출력값은?
#include <iostream>
using namespace std;
int main()
{
int p[4] = { 1,2,3,4 };
cout << *p << *(p + 1) << p[0] << p[1] << endl;
return 0;
}
1212
11.14) 다음 코드는 무엇이 잘못되었는가?
char* p;
cin >> p;
포인터 값이 아무 주소도 가리키지 않는다. 포인터는 꼭 초기화 해주어야 한다.
11.15) 출력은 무엇인가?
#include <iostream>
using namespace std;
int main()
{
char pCity[7] = "Dallas";
cout << pCity << endl;
cout << *pCity << endl;
cout << *(pCity+1) << endl;
cout << *(pCity+2) << endl;
cout << *(pCity+3) << endl;
return 0;
}
Dallas
D
a
l
l
11.16) 다음 출력은 무엇인가?
#include <iostream>
using namespace std;
int main()
{
char pCity[7] = "Dallas";
char cities[3][7] = {"Dallas","Atlant","Housto"};
cout << pCity[0] << endl;
cout << cities[0] << endl;
cout << cities[0][0] << endl;
return 0;
}
D
Dallas
D
'c, c++ > c++로 시작하는 객체지향 프로그래밍' 카테고리의 다른 글
12.1~12.5 (2) | 2024.04.25 |
---|---|
11.6~11.15 (0) | 2024.04.12 |
10.3~10.10 (2) | 2024.03.28 |
9.8~9.10)클래스에서 함수와 변수 (0) | 2024.02.02 |
9.6~9.7) 헤더파일 분리 (1) | 2024.01.28 |