배열의 크기는 선언할 때 정한다.
배열은 스택메모리 영역에서 관리하기 때문에 한번 정하고 나면 바뀔 수 없다.
동적할당을 이용하여 배열의 크기를 프로그램 실행 중에 바꿀 수 있는데 이는 힙영역에서 이루어지기 때문에 가능할 수 있게 한다.
가변배열을 사용하는 이유?
- 유연성
- 가변 배열은 크기를 동적으로 조정할 수 있으므로 다양한 입력 또는 상황에 대응하고 유연하게 대처할 수 있도록 한다.
- 메모리 효율성
- 가변 배열은 필요한 만큼의 메모리만 사용하여 낭비를 최소화한다.
- 자원 관리
- 필요하지 않은 요소들을 제거함으로써 메모리를 확보하고, 필요에 따라 새로운 요소들을 추가하여 메모리를 재사용할 수 있다.
- 성능 개선
- 일부 알고리즘에서는 입력 크기가 변경될 때마다 새로운 정적 배열을 할당하는 것보다 가변 배열을 사용하는 것이 성능상 이점이 있을 수 있다.
가변배열은 다음과 같이 만들 수 있다.
1. 구조체 만들기
2. 구조체 초기화하기
3. 포인터로 배열에 데이터 넣기
4. 공간 부족 시 기존 주소의 데이터를 복사
5. 기존 주소 삭제, 새로운 주소 할당
6. 전체 프로그램 사용 후 메모리 정리하기

분할 구현을 통해 가변배열을 만들어 보자
해당 해더에 구조체를 만들어 main 함수부분에서 불러온다.
typedef struct _tabArr
{
// 가변데이터을 가지는 구조체
// 해당 주소를 가르키는 포인터
int* pInt;
// 얼마나 넣었는지 알려주는 변수
int iCount;
//마지막 변수
int iMaxCount;
}tArr;
가변적으로 공간이 바뀌는 맴버변수는 포인터로 선언한다.
각 최소의 기능을 하는 변수를 만들 준비를 한다.
예상되는 최소기능
1. 구조체를 초기화한다.
2. 데이터를 추가한다.
3. 공간이 부족하면 증가시킨다.
4. 메모리를 해제시킨다.
위 기능을 고려 아래와 같이 함수 선언부를 작성한다.
각 함수들은 구조체 타입의 변수로 할당받을 것이다.
//초기화 함수
void InitArr(tArr* _pArr);
// 데이터를 추가 함수
void PushBack(tArr* _pArr, int _iData);
// 배열 메모리 해제함수
void ReleaseArr(tArr* _pArr);
//공간을 추가하는 ㅎ함수
void Reallocate(
선언한 함수들의 로직을 작성한다.
이렇게 분할구현하는 이유는 최소의 기능을 모듈화하면서 main함수에서의 가동성 향상하기 위해 분할한다.
아래 코드와 같이 함수를 실행 시 초기화 할 수 있도록 한다.
가변배열 부분은 int 자료형으로 정의 할 것이다.
malloc을 통해서 heap영역 메모리에 공간을 만든다. int형 자료형에 최초 2개의 int 자료형 데이터를 넣을 수 있는 공간을 생성하였다.
#include "Array.h"
#include <iostream>
void InitArr(tArr* _pArr) {
_pArr -> pInt = (int*) malloc(sizeof(int) * 2);
_pArr->iCount = 0;
_pArr->iMaxCount = 2;
}
데이터가 가득 차있는지 확인해주어야 한다.
가득차면 공간을 확장해야하는데 아래와 같이 공간을 확장하는 함수를 먼저 만들자
현재 iMaxCount의 2배 만큼의 공간을 새롭게 만들고 기존의 데이터를 새로운 포인터 변수에 저장한다. 기존 공간을 해제시키고 pInt가 pNew의 주소값을 할당받으면 공간이 더 커진 메모리 공간을 얻을 수 있다. iMaxCount를 2배 만큼 크게 만들어 다음 배열 크기 조정에 사용될 수 있도록 이용한다.
void Reallocate(tArr* _pArr)
{
// 1. 2배 더 큰 공간을 동적할당한다.
int* pNew = (int*)malloc(_pArr->iMaxCount * 2 * sizeof(int));
// 2. 기존 공간에 있던 데이터들을 새로 할당한 공간으로 복사한다.
for (int i = 0; i < _pArr->iCount; ++i)
{
pNew[i] = _pArr -> pInt[i];
}
//3. 기존 공간은 메모리 해제
free(_pArr->pInt);
//4. 배열이 새로 할당된 공간을 가리키게 한다.
_pArr->pInt = pNew;
//5. MaxCount 변경점 적용
_pArr->iMaxCount *= 2;
}
공간확장 함수를 만들었으면 데이터를 추가하는 함수를 만든다.
void PushBack(tArr* _pArr, int _iData)
{
//힙 영역의 데이터가 가득 참.
if (_pArr->iMaxCount <= _pArr->iCount)
{
// 공간 확장
Reallocate(_pArr);
}
//데이터 추가
// 마지막 공간으로부터 다음 주소값에 할당하고 count 증가
_pArr->pInt[_pArr->iCount++] = _iData;
}
힙 영역의 데이터가 가득 찼는지 여부는 count를 함수를 이용하면 된다. int 자료형으로 만들었기에 count와 iMaxCount 모두 int타입으로 만들어야 공간할당하는 변수와 동기화 시킬 수 있다.
데이터가 가득 찼으면 공간을 확장하는 함수로 공간을 증가시키고 마지막 공간으로부터 데이터를 할당한다.
iCount는 데이터가 저장된 만큼 정수가 저장되어있다. 해당 iCount에 데이터를 저장하고 다음 데이터 저장을 위해 후위연산으로 icount의 값을 증가시킨다.
전체 프로그램이 종료되었으면 메모리를 해제시켜 힙영역을 최적화하여야 한다.
이를 위한 함수는 다음과 같이 구현한다.
void ReleaseArr(tArr* _pArr)
{
free(_pArr ->pInt);
_pArr->iCount = 0;
_pArr->iMaxCount = 0;
}
free함수를 이용하여 pInt의 메모리영역을 해제시키고 iCount와 iMaxCount 변수들도 0으로 초기화 시킨다.
main 함수에서는 정의된 함수들을 이용해서 좀더 가독성이 높아졌다.
int main(){
// 구조체 변수 정의
tArr s = {};
InitArr(&s);
for (int i = 0; i < 10; ++i)
{
PushBack(&s, i);
}
for (int i = 0; i < s.iCount; ++i) {
printf("%d\n", s.pInt[i]);
}
// 힙 메모리 공간을 정리
ReleaseArr(&s);
return 0;
}
안녕하세요 혹시 이 C++ 가변배열은 따로 동적배열과는 관계가 없는 내용이 아닌가요?..???
구조체를 생성하지 않은 가변배열은.. 무엇인가요?
이런식으로 한번 생각해보았는데.
흠.. 위 내용에서 구조체를 생성해야 하는 이유가 무엇인가요..?