1. 동적 배열 (Dynamic Array)
- 정적 배열(static array)은 프로그램 실행(run-time)중 크기가 고정돼있다.
- 동적 배열의 크기는 프로그램 동작 중 동적으로 할당/해제될 수 있다. → 메모리 측면에서 효율적임!
1. new: 동적 메모리 할당에 쓰이는 키워드
double *numbers;
numbers = new double[4];
double *numbers = new double[4]; //선언과 할당 동시에
- numbers라는 이름의 배열, 원소들은 double형임
- numbers는 'new' 키워드로 double형 원소들을 크기(4)만큼 블락(메모리 공간)에 할당함
- 이 때 블락의 시작 주솟값(=[0]원소의 시작 주솟값)을 저장하는 포인터가 'numbers'가 된다. (동적 배열을 만든 것)
2. delete: 동적 메모리 해제에 쓰이는 키워드, 동적 할당을 해줬으면 반드시 메모리 할당 '해제'가 수반돼야 함!! (해제 안할 경우 메모리 잠식당함..)
delete[] numbers;
*) 1.1~1.2 example
#include <iostream>
#include <iomanip>
using namespace std;
void get_data(int *begin, int *end) {
cout << "Enter the elements: ";
for (int *curr = begin; curr != end; curr++)
cin >> *curr; //현재 주솟값의 메모리 공간으로 점프, 입력받음
}
void print(int *begin,int *end) {
cout << "Print elements: ";
for (int *curr = begin; curr != end; curr++)
cout << setw(4) << *curr;
cout << end;
}
int main() {
// int size = 3; 변수는 run-time중 바뀔 수 있음
const int size = 3; //변수x 상수o
int list[size] = { 10, 20, 30 }; //정적 배열에 크기를 변수로 주면 error
int length;
cout << "Enter the length: ";
cin >> length;
int *list2 = new int[length]; //동적 배열 선언(run-time중 필요에 의해 만들어짐)
int *begin = list2;
//배열의 시작주솟값 + 배열 크기(끝원소 다음 주솟값)
int *end = list2 + length;
get_data(begin, end);
print(begin, end);
delete[] list2; //동적 배열 할당 해제
return 0;
}
- 정적 배열(static array)의 크기를 변수로 줄 경우 에러가 발생한다. why? 변수는 언제든 값이 바뀔 수 있기 때문이다! 정적 배열의 크기는 한 번 픽스되면 바꿀 수 없기 때문에 값이 바뀔 가능성이 있는 변수를 사용하면 안됨! → const 상수로 크기를 지정해주는 것은 가능
- delete 키워드로 메모리 할당 해제를 안해줄 경우 메모리 공간 어딘가가 점유되어 프로그램이 끝나더라도 그 공간이 반환되지 않음, 다음 번에도 누적되어 실행된다.
2. 2차원 정적 / 동적 배열
*)2차원 테이블, 행렬, matrix라고 해서 메모리 상에서도 행-열로 저장되는 것은 아님, 메모리 상에서는 선형으로, 일렬로 나열되어 저장됨!
1. 2차원 정적 배열
int matrix1[2][3]; //2행 3열, 2 by 3, 2x3의 2차원 정적 배열->원소 6개
vector<vector<int>> vector(2, vector<int>(3)); //(복습)얘도 2차원 matrix
- 프로그램 종료까지 메모리에 계속 상주
2-1. 2차원 동적 배열 할당
int nRow = 2, nCol = 3; //동적 배열의 크기는 변수 사용해도 o!
int **matrix2 = new int*[nRow];
for (int i = 0; i < nRow; i++)
matrix2[i] = new int[nCol];
matrix2[0][0] = 1; //일차원배열의 각 방에 int값 넣어주기
matrix2[0][1] = 2;
matrix2[0][2] = 3;
matrix2[1][0] = 4;
matrix2[1][1] = 5;
matrix2[1][2] = 6;
- matrix2: 배열의 이름, 역시 포인터로 임시 타입 변환이 된다. 다른 점은 **2차원 배열** 이라는 것!! 이 때 matrix2는 'int형 원소들을 가리키는 포인터 변수를 가리키는 = 포인터 변수의 시작 주솟값을 저장하는' 포인터 변수로 타입 변환 된다는 것!! 즉 matrix2는 (int*)* 혹은 int** 형인 것이다!! (포인터 변수도 결국 메모리에 존재하는 애니까 당연히 '시작 주솟값'이 있다. 이 포인터의 주솟값을 저장하는 또다른 '포인터'가 이중 포인터, matrix2인 것이다.)
- matrix는 첫 번째 행을 가리킴 (0x00에는 0x01이 저장) (16진수는 예시)
- 0x01에는 1차원 배열 matrix2[0]의 시작 주솟값 0x10이, 0x02에는 matrix2[1]의 시작 주솟값 0x20이 저장 / matrix2[i]는 그 배열의 첫 번째 원소 [i][0]의 주솟값이기도 함 / matrix2[0](int*)→0x10, matrix2[1](int*)→0x20
- 각 1차원 배열은 배열의 첫 번째 원소([0][0], [1][0])를 가리킴 (시작 주소가 0x10, 0x20)
- int **matrix2 = new int*[nRow] : new를 통해 nRow개 만큼의 변수를 저장하는 메모리 블락을 할당함, 그 블락에 int*형 포인터 변수를 저장함(1차원 배열의 시작 주솟값을 저장)
- for문 : 1차원 동적 배열을 생성했을 때처럼 new를 통해 nCol개 만큼의 int형 변수를 블락에 할당(각 행=nRow에)
- matrix[0][0] = 1 ... : 1차원 배열의 각 방에 int형 원소들(1, 2, 3, ... , 6) 넣어주기
2-2. 2차원 동적 배열 해제
for (int i = 0; i < nRow; i++)
delete[] matrix2[i];
delete[] matrix2;
- for문 : matrix2[0], matrix2[1] 각각의 1차원 배열도 동적 배열이니 delete해주기
- delete[] matrix2 : matrix2도 2차원 동적 배열이므로 for문 바깥으로 나와서 delete해주기
*) 2.1~2.2-2 example
#include <iostream>
#include <iomanip>
using namespace std;
void print(int **matrix2,int nRow,int nCol) {
for (int row = 0; row < nRow; row++) {
for (int col = 0; col < nCol; col++)
cout << setw(4) << matrix2[row][col]; //.at()은 객체의 메소드이므로 객체 아닌 배열에는 못씀
cout << endl;
}
}
int main() {
int matrix[2][3];
//matrix2의 원소는 int를 가리키는 포인터변수
//그 포인터변수를 따라가면 정수형 변수
int nRow = 2, nCol = 3;
int **matrix2 = new int*[nRow];
for (int i = 0; i < nRow; i++)
matrix2[i] = new int[nCol];
matrix2[0][0] = 1;
matrix2[0][1] = 2;
matrix2[0][2] = 3;
matrix2[1][0] = 4;
matrix2[1][1] = 5;
matrix2[1][2] = 6;
print(matrix2, nRow, nCol);
for (int i = 0; i < nRow; i++)
delete[] matrix2[i];
delete[] matrix2;
return 0;
}
3. String 이전의 문자열 저장 → 문자(char)들의 '배열'
- string이 나오기 이전에는 문자열을 문자(char)들의 배열로 저장했다
- ex) const char* s = "oop" 을 실행하면 실질적으로 s = {'o', 'o', 'p', '\0'} 으로 저장됨.
- 이 때 '\0' == NULL 까지 저장함. (3개 + 1개의 문자)
- 문자열의 겨우 끝 주솟값(char *end = s + length; 이런 식으로)까지 함수로 보내줄 필요는 없음 (항상 마지막에는 null값이 저장되니까) (아래 예제에서 확인)
*) 3 example
#include <iostream>
#include <iomanip>
using namespace std;
bool find_char(const char *s, char ch) {
while (*s != '\0') { //문자열은 끝 주솟값 보내주지 않아도 됨!
if (*s == ch)
return true;
s++;
}
return false;
}
int main() {
const char *phrase = "this is a phrase";
//'A' : 65
//'a' : 97 (diff:32)
//ch++: '문자'도 아스키코드, 아스키코드 표 상에서 1씩 증가시킴
for (char ch = 'a'; ch <= 'z'; ch++) {
cout << ch << " is ";
if (!find_char(phrase, ch))
cout << "NOT ";
cout << "in (" << phrase << ")" << endl;
//ex) a is in (this is a phrase)
//ex) b is NOT in (this is a phrase)
//...z 까지 출력
}
return 0;
}
- phrase는 배열의 시작 주솟값을 저장한다.
- 문자열은 일반적인 배열로 취급하지 x, [const char *]은 하나의 문법으로 틀을 기억하기!
4. 포인터와 배열 표기법
p가 포인터일때,
p[i] == *(p+i)
p[i][j] == *(*(p+i)+j)
*)sample codes: https://github.com/halterman/CppBook-SourceCode
반응형
'코딩 > 객체지향프로그래밍' 카테고리의 다른 글
[C++] 클래스와 객체 (2) (0) | 2020.08.04 |
---|---|
[C++] 클래스와 객체 (1) (0) | 2020.08.02 |
[C++] 포인터, 벡터, 배열 (2) (0) | 2020.07.29 |
[C++] 포인터, 벡터, 배열 (1) (0) | 2020.07.27 |
[C++] String, File - 문자열과 파일 (2) (0) | 2020.07.25 |