180 likes | 311 Views
Chapter 10. 메모리 관리 및 저수준 자료구조 MANAGING MEMORY AND LOW-LEVEL DATA STRUCTURES. 10 장에서는 …. 지금까지는 표준 라이브러리에서 제공하는 vector 등의 컨테이너에 값을 저장했다 . 표준 라이브러리의 기능이 기본 언어가 제공하는 기능보다 더 융통성 있고 사용하기 쉽기 때문 … 10 장에서는 기본 언어가 제공하는 저수준 (lower-level) 테크닉 을 공부 컴퓨터 하드웨어 동작방식과 유사
E N D
Chapter 10 메모리 관리 및 저수준 자료구조 MANAGING MEMORY AND LOW-LEVEL DATA STRUCTURES
10 장에서는… • 지금까지는 • 표준 라이브러리에서 제공하는 vector 등의 컨테이너에 값을 저장했다. • 표준 라이브러리의 기능이 기본 언어가 제공하는 기능보다 더 융통성 있고 사용하기 쉽기 때문… • 10장에서는 • 기본 언어가 제공하는 저수준(lower-level) 테크닉을 공부 • 컴퓨터 하드웨어 동작방식과 유사 • 사용하기 더 어렵고 때로 위험하기도 하지만, 잘만 쓰면 때에 따라 표준 라이브러리보다 더 효율적일 수 있다. • 배열과 포인터, 동적 메모리 할당, 파일에서 읽고 쓰기 등
포인터와 배열 • 배열 (array) • 일종의 컨테이너로서, vector와 비슷하지만 덜 강력 • 멤버 함수 없음 (.size() 등) • 포인터 (pointer)는 일종의 임의접근 반복자 • 배열의 요소를 접근하는데 사용 • 인덱싱으로도 요소에 접근 가능 • 포인터와 배열은 • C/C++ 의 가장 기본적인 자료구조에 해당 • 포인터 없는 배열은 더 이상의 존재가치가 없으며, 배열이 있어야 포인터의 유용가치도 더 커진다
포인터(pointer) • 포인터: 객체의 주소(address)를 값으로 갖는데이터 타입 • 모든 객체는 고유의 메모리 주소를 가지고 있다 • 포인터 변수 선언: int *p;// p는 정수 변수를 가리키는 포인터 • 주소 연산자(address operator)& • x가 객체일 때, &x는 x의 주소 • 역참조 연산자(dereference operator) * • p가 객체의 주소일 때, *p는 그 객체 자체 • p=&x하면 p는 x의 주소를 갖게된다. • p는 x를 가리키는(point to)포인터라고 부름. • 널 포인터(null pointer) • 포인터가 상수 0 (NULL)을 값으로 가짐. • 아무것도 가리키지 않는 상태. 포인터 초기화에 사용 p x 2000 1000 1000 5 int x = 5; int *p; p = &x; cout << *p; cout << x; cout << p; cout << &x;
포인터 • 포인터 선언 • p의 타입은 “int에 대한 포인터” • 실행 결과? (*p)와 q는 int 타입을 갖는다 int *p, q; = int* p, q; = int *p; int q; int main() { int x = 5; // p points to x int* p = &x; cout << "x = " << x << endl; // change the value of x through p *p = 6; cout << "x = " << x << endl; return 0; }
배열 • 배열은 일종의 컨테이너 • 표준 라이브러리가 아닌 기본 언어의 일부 • 배열 요소의 개수는 컴파일 시 알 수 있어야 함 • 동적으로 늘어나거나 줄어들 수 없음 • 배열의 크기를 나타내는 타입은 size_t • 예 • 배열의 이름은 배열의 처음 요소에 대한 포인터 • *coords=1.5; == coords[0]=1.5; const size_t NDim=3; double coords[Ndim]; double coords[3]; 숙련된 프로그래머
포인터 산술 연산 • 포인터는 임의 접근 반복자 • p가 배열의 m번째 요소를 가리킨다면, • p+n은 m+n번째 요소를 가리킨다 • p-n은 m-n번째 요소를 가리킨다 • 포인터가 반복자이기 때문에, • coords를 벡터에 복사하기 위해서는 • copy()를 이용해서 또는 • 벡터는 두개의 반복자를 사용해 생성할 수 있으므로, vector<double> v; copy(coords, coords+NDim, back_inserter(v)); vector<double> v(coords, coords+Ndim);
인덱싱(indexing) • 포인터는 배열에 대한 임의접근반복자이므로, • 인덱싱도 지원한다 • p가 배열의 m번째 요소를 가리킨다면, p[n]은 • m+n번째 요소이다. • 즉, *(p+n)과 같다 • a가 배열이라면, 배열이름은 처음요소의 주소이므로, a[n]은 • 배열의 n번째 요소이다. 즉, *(0+n)
배열 초기화 • 초기화 예 const int month_lengths[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; • 배열 선언시 초기화 값을 부여할 때, 배열의 크기를 명시적으로 나타내지 않아도 된다. • 컴파일러가 배열 크기를 자동으로 계산해줌
문자열 리터럴 다시 살펴보기 • 문자열 변수 const char hello[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’}; • 리터럴 안의 문자들의 개수보다 하나 더 많은 요소를 포함. • 컴파일러는 문자열 끝에 ‘\0’를 붙여줌. 문자열이 끝나는 곳을 찾을 수 있도록 하기 위함 • 문자열 리터럴 • hello변수와 문자열 리터럴 “Hello”는 정확히 동일한 의미를 갖지만, • 변수와 문자열리터럴은 두 개의 구별되는 객체이므로, 서로 다른 주소를 갖는다. • 예) • 문자열의 길이를 알아내는 함수 size_t strlen(const char* p) { size_t size=0; while (*p++ != ‘\0’) ++size; return size; }
문자 포인터 배열 초기화하기 • 배열의 요소가 문자열인 예제 • 성적에 따라 학점 부여 (97이상 A+, 94이상 A, …) string letter_grade(double grade) { // range posts for numeric grades static const double numbers[] = { 97, 94, 90, 87, 84, 80, 77, 74, 70, 60, 0 }; // names for the letter grades static const char* const letters[] = { "A+", "A", "A-", "B+", "B", "B-", "C+", "C", "C-", "D", "F" }; static const size_t ngrades = sizeof(numbers)/sizeof(*numbers); for (size_t i = 0; i < ngrades; ++i) { if (grade >= numbers[i]) return letters[i]; } return "?\?\?"; } 각 요소가 char* 타입 (문자열)인 배열
main에 대한 인자들 • main함수도 함수이므로, 매개변수를 가질 수 있다. • 실행시킬 때,인자를 주어 동작 방식을 조종할 수 있다. • 실행 예: say.exe Hello, world • argc: 3 int main(int argc, char** argv) { // if there are command-line arguments, write them if (argc > 1) { // write the first argument cout << argv[1]; // write each remaining argument with a space before it for (int i = 2; i != argc; ++i) cout << " " << argv[i]; // `argv[i]' is a `char*' } cout << endl; return 0; }
파일 읽고 쓰기 • 표준 입력, 표준 출력 대신 파일에서 읽고 쓰기 • 많은 양의 입출력에 매우 유용 • 예, out.txt이라는 파일을 in.txt이라는 파일에 복사하는 프로그램 int main() { ifstream infile("in.txt"); ofstream outfile("out.txt"); string s; while (getline(infile, s)) outfile << s << endl; return 0; }
메모리 관리의 세 종류 if (…) { … string s; … … } 이때 생성되고, 블록이 끝날 때 해제 int* invalid_pointer() { int x; return &x; } 1) 지역 변수 • 정의부가 실행되는 시점에 생성되고, 정의부를 포함하는 블록의 끝에서 자동으로 해제. 자동 메모리 관리. • 이렇게 하면 문제 발생! 14
메모리 관리의 세 종류 // 이 함수는 OK int* pointer_to_static() { static int x; return &x; } 2) 정적 할당 (static allocation) • 처음 호출되기 전 어느 시점에 단 한번만 할당되고, 프로그램 실행 동안에 해제되지 않음 15
객체 할당 및 해제 p 42 43 3) 동적 할당 (dynamic allocation) T가 객체의 타입이라면, • new T는 타입 T의 객체를 할당하라는 명령 • 그 객체는 디폴트-초기화: 초기화 안되거나, 디폴트 생성자 실행 • 새롭게 할당된 (이름 없는) 객체에 대한 포인터를 돌려줌 • new T(args)하면 할당된 객체를args로 초기화 • 객체를 해제할 때는, delete p; • 예) int* p=new int(42); // *p는 42라는 값을 갖는 정수 객체 ++*p; // 객체의 값은 43이 된다. delete p; // 객체를 해제한다. 16
메모리의 영역 구분 동적 할당 힙(heap) 영역 new delete 스택(stack) 영역 지역변수 레지스터변수 정적(static) 영역 전역변수 정적변수 프로그램 코드
배열 할당 및 해제 char* duplicate_chars(const char* p) { // 널 추가해야하므로 +1 size_t length=strlen(p)+1; char* result=new char[length]; // 새로 할당된 공간에 복사, 첫째 요소에 대한 포인터 리턴 copy(p, p+length, result); return result; } • 배열 할당 예: new int[SIZE]; int* p=newint[1000]; // int p[1000];과 비슷 … delete[] p; // 배열을 해제한다. • 예) 문자열 복사 함수 18