C++/class
class_lab10.cpp 포인터의 개념
year.number
2022. 5. 25. 17:47
포인터의 주 사용 목적
1. 용량이 큰 데이터를 전달하는 경우 -> 비용이 큼
데이터 전달이 아니라 데이터 주소만 전달하기 때문에 시간, 메모리 등 비용적인 측면에서 효율적
2. call by value
함수의 매개변수로 어떤 값을 전달할 때 함수 내에서 매개변수를 수정하는 경우 변경되지 않음 (main함수 내에서는 변경O)
ex) main 함수 내에서의 x,y -> 함수에서 x++, y++을 해도 돌아왔을 때 변화X
그동안은 전역변수를 선언해서 사용하는 편법(?)을 이용했지만 포인터로 해결!
#include <iostream>
#include <iomanip>
using namespace std;
/*
1. 포인터 변수란?
주소를 가지고 있는 변수
변수, 베열은 RAM(주기억장치)에 저장된다.
int i의 메모리를 어디에 할당할지는(주소) 운영체제가 결정한다.
[직접 참조] int i = 10;
[간접 참조] int i가 4번지일 때, 4번지 = 10;
4번지라는 주소를 알기 위해 필요한 것: 주소 연산자(&), 간접 참조 연산자(*)
*/
//함수 원형
void printArray(int x[], int size){
for (int i = 0; i<5; i++)
cout << setw(5) << x[i];
cout << endl;
}
void intArray_i(int x[], int size){
//배열을 {0,1,2,3,4}로 초기화
for (int i = 0; i<5; i++){
x[i] = i;
cout << setw(5) << x[i];
}
}
void intArray_p(int *x, int size){
//배열을 {0,1,2,3,4}로 초기화
for (int i = 0; i<5; i++){
*(x+i) = i;
cout << setw(5) << *(x+i);
}
}
//함수 원형
//함수 오버로딩: 원래 함수 이름이 같아도 매개변수만 다르면 OK였지만 포인터는 XXXX!!
//생긴 건 다르게 생겼지만 int x[]도 주소고 int *x[]도 주소라서 컴파일러가 구분X
void printArray2(int *x, int size){
for (int i = 0; i<5; i++)
cout << setw(5) << *(x+i);
cout << endl;
}
int main(){
int i = 10; //운영체제가 주소를 정해줌 (ex. 4바이트, 4번지)
char c = 69; //1바이트, 8번지
double f = 12.3; //8바이트, 12번지
//주소는 고정된 값이 아니기 때문에 항상 알아봐야 한다. -> 주소 연산자(&) 필요
//&i = 4;
//*p = 10;
//주소 출력
cout << "i의 주소: " << &i << endl; //1000
cout << "f의 주소: " << &f << endl; //2000
//16진법으로 출력
cout << "i의 주소: " << (long long)&i << endl; //1000
cout << "f의 주소: " << (long long)&f << endl; //2000
//i의 저장값
cout << i << endl; //10, 직접 참조
cout << *(&i) << endl; //10, 간접 참조, i의 주소를 먼저 찾고(&i)->거기에는 뭐가 있을까?*(&i)
// ====================================================================
//i의 주소는 매번 바뀜 -> 저장했다가 나중에 간접 참조를 하고 싶을 때
//변수에 주소를 저장: 주소가 12자리이기 때문에 int는 탈락, long long 자료형의 변수 사용
cout << "\n포인터의 선언" << endl;
/*
[에러] 주소를 일반 변수에 저장할 수 없음
long long juso;
juso = &i; //변수에 i의 주소 저장
*/
// 주소를 저장할 수 있는 변수 필요 -> 주소형 변수(포인터 변수)
int *pi; //정수형 주소를 저장할 수 있는 포인터 변수 pi 선언, pi에는 정수형 주소만 저장할 수 있다
// * 연산자: 곱셈, 간접참조, 포인터 선언 3가지로 선언
pi = &i; //포인터 변수에 i의 주소 저장,
cout << "i의 저장값: " << i << endl; //10
cout << "pi의 참조값 *pi: " << *pi << endl; //10(i값)
cout << "i의 주소의 참조값 *(&i): " << *(&i) << endl;
cout << "pi의 저장값: " << pi << endl; //i의 주소
// ====================================================================
//변수 i에 1 더하기
i++;
cout << "\ni + 1의 저장값: " << i << endl;
i = i + 1;
cout << "i + 1의 저장값: " << i << endl;
i = *pi + 1;
cout << "i + 1의 저장값: " << i << endl;
*pi = *pi + 1;
cout << "i + 1의 저장값: " << i << endl;
(*pi)++;
cout << "i + 1의 저장값: " << i << endl;
// ====================================================================
/*
[주의사항]
1. 포인터 타입과 저장하는 주소의 타입이 일치해야 한다.
ex)
char c;
pi = &c; // pi는 정수형 포인터 변수라서 할당할 수 없다.
2. 포인터(초기화) 누락 -> 문법 상 문제는 없지만 쓰레기값 출력
int *p;
*p = 100;
3. NULL 주소 참조
int *p = NULL; //비어있는 주소로 초기화
*p = 100;
*/
// ====================================================================
//포인터의 크기
cout << "\n정수형 변수 i의 크기: " << sizeof(i) << endl; //4
cout << "정수형 변수 c의 크기: " << sizeof(c) << endl; //1
cout << "정수형 변수 f의 크기: " << sizeof(f) << endl; //8
double d;
char *pc = &c;
double *pd = &d;
cout << "\n문자형 포인터 pc의 크기 " << sizeof(*pc) << endl; //
cout << "정수형 포인터 pi의 크기: " << sizeof(*pi) << endl; //
cout << "double형 포인터 pd의 크기: " << sizeof(*pd) << endl; //
// ====================================================================
//포인터의 연산
cout << "\n포인터의 연산" << endl;
//포인터++의 의미?
//=>다른 데이터의 주소! **번지수를 1 증가 시키라는 얘기가 아님!
// pi = 100,
// pi + 1 = 104,
// pi - 1 = 96
float *pf = (float *)100;
pc = (char *)100;
pi = (int *)100;
pd = (double *)100;
cout << "pc의 저장값: " << (long long)pc << endl; //문자형 100번지
cout << "pi의 저장값: " << (long long)pi << endl; //정수형 100번지
cout << "pf의 저장값: " << (long long)pf << endl; //플롯형 100번지
cout << "pd의 저장값: " << (long long)pd << endl; //더블형 100번지
cout << "포인터 연산 => 포인터에 ++(증감 연산자)를 했을 때" << endl;
pc++;
pi++;
pf++;
pd++;
cout << "\npc의 저장값: " << (long long)pc << endl; //문자형 101번지
cout << "pi의 저장값: " << (long long)pi << endl; //정수형 104번지
cout << "pf의 저장값: " << (long long)pf << endl; //플롯형 104번지
cout << "pd의 저장값: " << (long long)pd << endl; //더블형 108번지
// ====================================================================
// @@이해 필요
// 112 - 108 = 8이 아니라 112과 104 사이에는 정수가 두 개 있으므로 결과는 2
int *pi2 = (int *)112;
cout << "pi2 - pi = " <<pi2 - pi << endl; //112 - 104 => 8???
// ====================================================================
// 포인터와 증감 연산자
int j;
i = 3;
pi = &i;
cout << "\npi의 저장값: " << (long long)pi << endl;
j = *pi++;
cout << "\nj = *pi++ 실행 후 " << endl;
cout << "i의 저장값: " << i << endl; //3
cout << "j의 저장값: " << j << endl; //3
cout << "pi의 저장값: " << (long long)pi << endl; // +4번지만큼 증가
// ====================================================================
i = 3;
pi = &i; //초기 상태로 복구
j = (*pi)++;
cout << "\nj = (*pi)++ 실행 후 " << endl;
cout << "i의 저장값: " << i << endl; //4
cout << "j의 저장값: " << j << endl; //3
cout << "pi의 저장값: " << (long long)pi << endl;
// ====================================================================
i = 3;
pi = &i; //초기 상태로 복구
j = *++pi;
cout << "\nj = *++pi; 실행 후 " << endl;
cout << "i의 저장값: " << i << endl; //3
cout << "j의 저장값: " << j << endl; //쓰레기값
cout << "pi의 저장값: " << (long long)pi << endl; // +4번지만큼 증가
// ====================================================================
i = 3;
pi = &i; //초기 상태로 복구
j = ++*pi;
cout << "\nj = ++*pi; 실행 후 " << endl;
cout << "i의 저장값: " << i << endl; //4(참조값 증가)
cout << "j의 저장값: " << j << endl; //4
cout << "pi의 저장값: " << (long long)pi << endl;
// ====================================================================
// 배열과 포인터 [오늘의 핵심]
/*
함수에 배열 전달
함수 호출: 함수명(배열명[], ...)
함수를 호출할 때 배열의 크기를 쓰지 않는 이유 -> 배열명이 배열의 시작 주소이기 때문이다.
!!! 배열명 = 배열의 시작 주소
배열명은 포인터다.
포인터는 주소를 바꿀 수 있지만,
배열명은 배열의 시작 주소를 항상 알려줘야 하기 때문에 const pointer여서 수정 불가
*/
int point[5] = {1,2,3,4,5};
cout <<"\n배열의 시작주소는 제일 앞 원소의 주소(&point[01]) = " << (long long)&point[0] << endl;
cout << "배열의 시작주소는 배열명(point) = " << (long long)point << endl; //소오름...
//*(point+i) = point[i]
// 주소로 표현 = index로 표현
//예시
//point = (int *)100; -> 배열명이 const pointer여서 오류
cout << "\n*(point + 0) = " <<*(point + 0) << endl; //1;
cout << "point[0] = " << point[0] << endl; //1;
cout << "*(point + 1) = " <<*(point + 1) << endl; //2;
cout << "point[1] = " << point[1] << endl; //2;
//배열 전체 출력
cout << "\n배열 전체 출력(index+loop): " << endl;
for (int i = 0; i<5; i++)
cout << setw(5) << point[i];
cout << endl;
cout << "\n배열 전체 출력(포인터): " << endl;
for (int i = 0; i<5; i++)
cout << setw(5) << *(point+i);
cout << endl;
// ====================================================================
// 포인터와 함수
// 배열을 함수에 포인터로 전달하는 방법
//배열 전체 출력
cout << "\n배열 전체 출력(index+function): " << endl;
//printArray(point[5], 5); //X
//printArray(point[], 5); //X
printArray(point, 5);
cout << "\n배열 전체 출력(포인터+function): " << endl;
//printArray(&point[], 5); //OK
printArray2(point, 5);
//배열을 {0,1,2,3,4}로 초기화(배열 이용)
for (int i = 0; i<5; i++){
point[i] = i;
}
cout << "\n배열의 초기화: " << endl;
printArray(point, 5);
//배열을 {0,1,2,3,4}로 초기화(포인터 이용)
for (int i = 0; i<5; i++){
*(point + i) = i;
}
printArray(point, 5);
intArray_i(point, 5); //배열
cout << endl;
intArray_p(point, 5); //주소
cout << endl;
// 시간 되면 2차 배열과 포인터
// 과제 설명
//1. lab8의 함수 원형을 다 포인터로 바꾸기
//int x[] -> int *x
//merge 함수는 할 필요X, 함수 원형, 함수 선언만 바꾸기
//main 함수도 손 댈 일이 없음(배열명이 배열의 주소였기 떄문에 호출에는 수정사항X)
return 0;
}

