-
class_lab10.cpp 포인터의 개념C++/class 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; }
'C++ > class' 카테고리의 다른 글
[C++] 클래스에 대한 이해(Car.cpp) (0) 2022.06.24 [C++] 함수가 여러 개의 값을 반환해야하는 경우 (0) 2022.06.15 [C++] 외부 파일에서 데이터 읽어오기(fstream, infile) (0) 2022.06.14 [C++] 문자열(string) (0) 2022.06.09 class_lab11.cpp 참조에 의한 호출 (0) 2022.06.08