2024. 3. 17. 14:39ㆍProgramming Language/C++
• 동적 배열이라고도 하며, 메모리 공간을 계속 확장하면서 데이터를 넣는 형태의 자료구조이다.
• 가변 배열을 사용하려면 동적 할당 즉, 힙 영역을 사용해야 한다.
• 가변 배열을 잘 설계해 놓으면 여러 개 만들 수 있다.
▶ 정적 배열 vs. 가변 배열
정적 배열(Static Array) | 가변 배열(Dynamic Array, 동적 배열) | |
설명 | 배열의 크기가 고정되어있으며, 컴파일 시 크기가 결정된다. | 배열의 크기를 조절할 수 있다. 프로그램 실행 중 메모리를 힙 영역에 할당하여 생성된다. |
메모리 영역 |
스택 또는 데이터 영역에 저장된다. | 힙 영역에 저장된다. |
특징 | 한 번 크기가 결정되면 프로그램 실행 중 크기를 변경할 수 없다. 지역 변수로 선언된 정적 배열은 스택 영역에 저장된다. static 키워드를 사용하거나 전역 변수로 선언된 배열은 데이터 영역에 저장된다. |
배열의 크기를 런타임에 변경할 수 있다. new 또는 malloc()을 사용하여 메모리를 할당하고, delete로 해제한다. 동적으로 할당된 메모리는 크기 변경이 불가능하지만, 다른 메모리를 재할당하는 등의 방법으로 크기를 조정할 수 있다. |
▷ 정적 배열
void func() {
int arr[10]; // 스택 영역에 저장된다.
}
static int staticArr[10]; // 데이터 영역에 저장된다.
int main()
{
int iArr[10] = {}; // 스택 영역에 저장된다.
return 0;
}
▷ 가변 배열
int main() {
int size = 5;
int* dynamicArray = new int[size]; // 동적 배열 할당 (힙 영역)
// 필요에 따라 다른 크기로 재할당
delete[] dynamicArray;
size = 10;
dynamicArray = new int[size]; // 새로운 크기로 다시 할당
delete[] dynamicArray; // 메모리 해제
return 0;
}
가변 배열 만드는 법
① 동적 배열의 초기화
• 동적 배열을 사용하기 위해 포인터를 선언하고, 필요한 크기의 메모리를 동적으로 할당한다.
• 구조체(가변 배열 자료형)의 경우 포인터(힙 영역의 시작 주소값), 현재 데이터 개수, 최대치를 멤버로 포함한다.
② 데이터 추가
• 배열에 데이터를 추가할 때 추가하기 전에 배열이 꽉 찼는지 확인하고, 배열의 현재 크기를 증가시키며 데이터를 추가한다.
③ 공간이 모자르면 재할당한다.
• 현재 배열의 크기가 최대라면, 더 큰 메모리 공간을 동적으로 할당한다.
보통은 기존 용량의 2배로 확장한다.
④ 데이터 복사 및 기존 메모리 해제
• 기존 배열의 데이터를 새 배열로 복사한 후, 기존 메모리를 해제한다.
⑤ 데이터 추가 완료 및 크기 변경
• 새 데이터를 추가하고 배열 크기를 변경한다.
⑥ 추가적인 데이터 추가 반복
• 이후 데이터 추가 시, 위 과정을 반복한다.
▷ 예시
#include <iostream>
using std::cout;
using std::endl;
int main() {
int init = 2; // 초기 배열 크기
int* arr = new int[init]; // 가변 배열 초기화
int size = 0; // 현재 데이터 개수
int capacity = init; // 배열 용량
// 데이터 삽입 함수 역할
void insertData(int*& arr, int& size, int& capacity, int value) {
if (size == capacity) {
int newCapacity = capacity * 2;
int* newArr = new int[newCapacity];
for (int i = 0; i < size; ++i) {
newArr[i] = arr[i];
}
delete[] arr;
arr = newArr;
capacity = newCapacity;
}
arr[size++] = value;
}
// 데이터 삽입 테스트
insertData(1);
insertData(2);
insertData(3); // 재할당 필요
insertData(4);
// 배열 데이터 출력
cout << "배열 데이터";
for (int i = 0; i < size; ++i) {
cout << arr[i] << endl;
}
cout << endl;
// 가변 배열 메모리 해제
delete[] arr;
return 0;
}
▽ 출력 결과
1
2
3
4
C++에서는 동적 배열보다std::vector
를 사용하는 것이 더 효율적이고 간단하다.std::vector
는 위 과정을 내부적으로 처리하고, 메모리 관리와 크기 확장을 자동화한다.
가변 배열 만들 수 있는 경우 (동적 할당 사용)
아래 예시들의 경우 배열 자체는 힙에 동적으로 할당되며, 차이점은 메모리 관리 방식이다.
▷ 지역 변수
void func() {
int size = 10;
int* dynamicArr = new int[size]; // 힙 영역에 동적으로 할당된 배열
// 동적 배열 사용
delete[] dynamicArr; // 메모리 해제
}
// 포인터 dynamicArr는 지역 변수이지만, 배열 자체는 힙에 동적으로 할당되고 delete[]로 해제하기 전까지 유지된다.
▷ 전역 변수
int* globalArr;
void func() {
int size = 10;
globalArr = new int[size]; // 동적 배열 할당
// 전역 배열 사용
}
int main() {
func();
delete[] globalArr; // 메모리 해제
return 0;
}
// 포인터 globalArr는 전역 변수이다.
▷ 정적 변수
void func() {
static int* staticArr = nullptr; // static 포인터
int size = 10;
if (staticArr == nullptr) {
staticArr = new int[size]; // 한 번만 동적 할당
}
// static 배열 사용
// 프로그램 종료 시 메모리 해제
}
int main() {
func();
delete[] staticArr; // 한 번만 메모리 해제
return 0;
}
// 포인터 staticArr는 정적 변수이고 함수 호출 간에 그 값을 유지하지만, 배열은 힙에 동적으로 할당된다.
▷ 외부 변수
ex1.cpp
extern int* externalArr; // 외부 포인터 선언
void func() {
int size = 10;
externalArr = new int[size]; // 동적 배열 할당
}
ex2.cpp
int* externalArr; // 외부 포인터 정의
// 포인터 externalArr는 외부 변수이다.
가변 배열을 만들 수 없는 경우
• 변수로는 가변 배열을 만들 수 없다.
멤버로 가지고 있는 배열의 크기가 정해져있지 않으면 컴파일러 입장에서 배열의 길이를 확신할 수 없다.
▷ 지역 변수
#include <stdio.h>
int main()
{
// 예시 1
int a = 100; // 지역 변수 a : main() 함수 실행 시 생성되고 종료 시 해제된다.
int iArr[a] = {}; // 오류 => 컴파일러 입장에서 배열의 길이를 확신할 수 없다.
// 예시 2
int b = 100; // 지역 변수 b : main() 함수 실행 시 생성되고 종료 시 해제된다.
int i = 0;
scanf_s("%d", &b);
int iArr[b] = {}; // 오류 => 컴파일러 입장에서 배열의 길이를 확신할 수 없다.
return 0;
}
▷ 전역 변수
전역 변수는 런타임 중에 값이 변경될 수 있다.
따라서 전역 변수로 가변 배열을 만들려고 하면 구조체의 크기가 확정적이지 않고 컴파일러 입장에서도 정보가 명확하지 않기 때문에 오류가 발생한다.
// int는 4byte로 고정이다.
int gI = 100;
typedef struct _tagST
{
// int형으로 선언한 배열의 길이는 gI이지만, 그 크기가 확정적이지 않다.
int iArr[gI]; // 오류
} ST;
▷ 정적 변수
#include <stdio.h>
int main() {
static int b = 100; // static 변수 b
int iArr[b] = {}; // 오류 => 배열 크기를 결정할 수 없다.
return 0;
}
b
값이 런타임에 결정되기 때문에 컴파일 시 배열의 크기를 알 수 없어서 오류가 발생한다.
'Programming Language > C++' 카테고리의 다른 글
[C++] 정렬과 버블 정렬 구현, 함수 포인터 (0) | 2024.03.17 |
---|---|
[C/C++] 연결 리스트 구현 (0) | 2024.03.17 |
[C++] 동적 할당, malloc()/free() vs. new/delete (0) | 2024.03.15 |
[C++] 아스키(ASCII) 코드 (0) | 2024.03.14 |
[C++] void 포인터(void*) (0) | 2024.03.14 |