본문 바로가기

코딩/객체지향프로그래밍

[C++] 포인터, 벡터, 배열 (3)

출처: http://tcpschool.com/c/c_array_oneDimensional

 


 

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

반응형