[C/C++] 가변 배열 - 구조체/분할 구현
2024. 3. 28. 19:21ㆍProgramming Language/C++
아래 링크 클릭 시 해당 본문으로 이동
가변 배열 - C
구조체와 동적 할당을 이용하여 가변 배열 자료형 만들기
Arr.h
#pragma once
// 가변 배열 자료형 tArr (int)
typedef struct _tagArr
{
int* pInt;
int iCount;
int iMaxCount;
} tArr;
// 배열 초기화 함수
// 반환 타입이 필요없기 때문에 void
// 주소를 넘겨줘야 되기 때문에 인자 타입은 배열의 주소인 tArr*
void InitArr(tArr* _pArr);
// 데이터 추가 함수
// 인자 타입은 배열의 주소 tArr*
// int 데이터를 추가해야 되므로 int형 인자 _iData 추가
void PushBack(tArr* _pArr, int _iData);
// 공간 추가 확장(재할당) 함수
// 메모리 공간이 꽉 차면 확장시키며, 데이터를 넣는 과정에서 이 함수를 호출한다.
// main()에서 이 함수를 호출할 수 있는 이유는 참조한 현재 Arr.h에 함수를 선언했기 때문이다.
// 하지만 링크 단계에서 연결이 되므로 내가 원하지 않은 결과가 나올 수 있는데,
// main()에는 이 함수의 존재를 알 수 없게 하기 위해 헤더 파일에 Reallocate()를 명시하지 않으면 이를 방지할 수 있다.
// 가변 배열에서도 Arr.cpp에 정의를 했기 때문에 문제가 되지 않는다.
void Reallocate(tArr* _pArr);
// 배열 메모리 해제 함수
void ReleaseArr(tArr* _pArr);
Arr.cpp
#include <cstdlib>
#include "Arr.h"
// 배열 초기화
void InitArr(tArr* _pArr)
{
// -> (화살표 연산자) : 역참조 후 멤버 접근
_pArr->pInt = (int*)malloc(sizeof(int) * 2); // 힙 영역의 시작 주소값
_pArr->iCount = 0; // 현재 들어와있는 데이터 개수
_pArr->iMaxCount = 2; // 최대치
}
// 공간 추가 확장(재할당)
void Reallocate(tArr* _pArr)
{
// 1. 2배 더 큰 공간을 동적할당한다.
// 2칸이면 4칸, 4칸이면 8칸, 8칸이면 16칸, ...
int* pNew = (int*)malloc(_pArr->iMaxCount * 2 * sizeof(int));
// malloc()으로 할당받은 주소를 pInt로 받으면 발생하는 문제
// 시작 주소를 가리키는 pInt는 새로 할당받은 메모리 공간의 시작 주소를 받게 되어서 원래 시작 주소가 아니게 된다.
// 그래서 위의 경우 주소는 지역변수로 받아야 한다.
// 2. 기존 공간에 있던 데이터들을 새로 할당한 공간으로 복사시킨다.
for (int i = 0; i < _pArr->iCount; ++i)
{
pNew[i] = _pArr->pInt[i];
}
// 3. 기존 공간은 메모리 해제
free(_pArr->pInt);
// 4. 배열이 새로 할당된 공간을 가리키게 한다.
_pArr->pInt = pNew;
// 5. iMaxCount 변경점 적용
_pArr->iMaxCount *= 2;
}
// 데이터 추가
void PushBack(tArr* _pArr, int _iData)
{
// 힙 영역에 할당한 공간이 다 찬다면
if (_pArr->iMaxCount <= _pArr->iCount) // 만에 하나 잘못된 값이 들어와서 개수를 초과한 경우, 재 할당이 안 일어날 가능성이 있다.
{
// 공간이 모자를 경우 재할당 (공간 확장 함수 Reallocate())
Reallocate(_pArr);
}
// 데이터 추가
// 다음 데이터를 추가할 인덱스값 = iCount
// 가변 배열이 멤버로 들고 있는 pInt의 주소 변수로 가서 인덱스 접근을 할건데 그때 사용할 인덱스가 본인이 사용하는 iCount값이다.
// 접근해서 그 위치에 수정하고나서 iCount값이 증가해야하므로 후위연산자 사용
// 입력까지 다 끝나고 나서 iCount값 증가시키기
_pArr->pInt[_pArr->iCount++] = _iData;
}
// 배열 메모리 해제
void ReleaseArr(tArr* _pArr)
{
free(_pArr->pInt);
_pArr->iCount = 0;
_pArr->iMaxCount = 0;
}
main.cpp
#include <cstdlib>
#include <stdio.h>
#include "Arr.h"
// 가변 배열 : 데이터를 계속 확장해가면서 넣는 형태의 자료구조
int main()
{
tArr s1 = {};
// 배열 초기화
InitArr(&s1);
// malloc()으로 *내가 원하는 주소*에 메모리를 할당할 수는 없다.
// 동적할당으로 되돌려준 주소에는 요청한 만큼의 메모리가 할당되어있다.
// 할당해준 공간만 사용해야되는데, 만약 포인터로 해당 공간이 아닌 다른 공간으로 넘어가면 다른 객체가 사용하고 있는 공간을 침범하고 훼손시킬 수 있어 문제가 발생한다.
// ex. 할당한 공간은 10칸인데 실수로 100칸을 넘어간 경우
// c++은 안전 장치가 없다.
// 실수로 조작한 순간에 문제가 발생하는 것이 아니라 뒤늦게 그 값을 사용할 때 문제가 발생하므로 어디서 문제가 발생했는지 찾기가 어렵다.
// 또한 할당하지 않은 곳의 메모리를 사용하는 경우도 문제이다.
// 운영체제는 쓰이지 않은 공간으로 인식하고 있는 그곳을 사용했을 때 발생하는 문제도 나중에서야 발견하게 된다.
// 이런 오류를 *힙 손상(Heap Corruption)*이라고 하며, 한 박자 늦게 또는 한참 뒤에 뜬금없이 문제가 발생한다.
// 비슷한 경우 : 배열 스택
// 지역 변수인데도 배열(배열도 사실은 포인터이다.) 인덱스를 잘못 접근하면 다른 변수의 스택 메모리 공간에 침범한다.
// 재할당
// 처음에 동적할당 받을 때 충분히 넉넉한 공간을 잡아줘야 한다.
// 2칸을 사용하다가 2칸을 더 할당받고 싶을 때 2칸 뒤에 2칸을 이어붙이는 것이 아니라 4칸을 새로 할당받고 원래 2칸을 복붙한다.
// 데이터 추가
for (int i = 0; i < 10; ++i)
{
PushBack(&s1, i); // s1에 0~9까지 넣어라.
}
for (int i = 0; i < s1.iCount; ++i)
{
printf("%d\n", s1.pInt[i]); // i번째 데이터 출력
}
// 배열 메모리 해제
ReleaseArr(&s1);
return 0;
}
분할 구현 이용해서 가변 배열 자료형 만들기
Arr.h
#pragma once
// 동적 배열을 정의하는 구조체
typedef struct _tagArr {
int* pInt; // 가변 배열을 저장할 포인터
int iCount; // 배열에 현재 저장된 데이터 개수
int iMaxCount; // 배열의 최대 용량
} tArr;
// 배열 초기화
void InitArr(tArr* _pArr);
// 재할당
void Reallocate(tArr* _pArr, int newMax);
// 데이터 추가
void PushBack(tArr* _pArr, int value);
// 메모리 해제
void ReleaseArr(tArr* _pArr);
Arr.cpp
#include "Arr.h"
#include <cstdlib>
// 배열 초기화
void InitArr(tArr* _pArr) {
_pArr->pInt = (int*)malloc(sizeof(int) * 2); // 2개의 정수를 저장할 메모리 할당
_pArr->iCount = 0; // 처음에는 데이터가 없으므로 iCount는 0
_pArr->iMaxCount = 2; // 배열의 용량은 2로 초기화
}
// 재할당
void Reallocate(tArr* _pArr, int newMax) {
_pArr->pInt = (int*)realloc(_pArr->pInt, sizeof(int) * newMax); // 메모리 재할당
_pArr->iMaxCount = newMax; // 최대 용량 업데이트
}
// 데이터 추가
void PushBack(tArr* _pArr, int value) {
if (_pArr->iCount == _pArr->iMaxCount) { // 배열이 가득 찼으면
Reallocate(_pArr, _pArr->iMaxCount * 2); // 배열 크기를 두 배로 늘린다.
}
_pArr->pInt[_pArr->iCount] = value; // 새로운 값 추가
_pArr->iCount++; // 데이터 개수 증가
}
// 메모리 해제
void ReleaseArr(tArr* _pArr) {
free(_pArr->pInt); // 동적 메모리 해제
_pArr->pInt = nullptr; // 포인터를 nullptr로 설정하여 안전하게 만든다.
_pArr->iCount = 0; // 데이터 개수 초기화
_pArr->iMaxCount = 0; // 최대 용량 초기화
}
main.cpp
#include "Arr.h"
#include <iostream>
using namespace std;
int main() {
tArr s;
// 배열 초기화
InitArr(&s);
// 배열에 데이터 추가
PushBack(&s, 10);
PushBack(&s, 20);
PushBack(&s, 30);
// 배열의 내용 출력
for (int i = 0; i < s.iCount; i++) {
cout << s.pInt[i] << endl;
}
// 메모리 해제
ReleaseArr(&s);
return 0;
}
▽ 출력 결과
10
20
30
가변 배열 - C++
구조체와 동적 할당을 이용하여 가변 배열 자료형 만들기
Arr.h
#pragma once
// 가변 배열 자료형 tArr (int)
typedef struct _tagArr
{
int* pInt;
int iCount;
int iMaxCount;
} tArr;
// 배열 초기화 함수
void InitArr(tArr* _pArr);
// 데이터 추가 함수
void PushBack(tArr* _pArr, int _iData);
// 공간 추가 확장(재할당) 함수
void Reallocate(tArr* _pArr);
// 배열 메모리 해제 함수
void ReleaseArr(tArr* _pArr);
Arr.cpp
#include "Arr.h"
// 배열 초기화
void InitArr(tArr* _pArr) {
_pArr->pInt = new int[2]; // new[]를 사용하여 메모리 할당
_pArr->iCount = 0; // 현재 입력된 데이터 개수
_pArr->iMaxCount = 2; // 최대 크기
}
// 공간 추가 확장 (재할당)
void Reallocate(tArr* _pArr) {
// 1. 두 배 크기로 새로운 공간을 동적으로 할당
int* pNew = new int[_pArr->iMaxCount * 2]; // new[]를 사용하여 메모리 할당
// 2. 기존 공간의 데이터를 새로 할당한 공간으로 복사
for (int i = 0; i < _pArr->iCount; ++i) {
pNew[i] = _pArr->pInt[i];
}
// 3. 기존 공간의 메모리를 delete[]로 해제
delete[] _pArr->pInt;
// 4. 배열을 새로 할당된 공간을 가리키도록 변경
_pArr->pInt = pNew;
// 5. iMaxCount에 변경 사항 적용
_pArr->iMaxCount *= 2;
}
// 데이터 추가
void PushBack(tArr* _pArr, int _iData) {
// 힙 영역에 할당된 공간이 꽉 찼다면,
if (_pArr->iMaxCount <= _pArr->iCount) {
// 공간이 부족하면 재할당 (공간 확장 함수 Reallocate() 호출)
Reallocate(_pArr);
}
// 데이터 추가
_pArr->pInt[_pArr->iCount++] = _iData;
}
// 메모리 해제
void ReleaseArr(tArr* _pArr) {
// delete[]로 메모리 해제
delete[] _pArr->pInt;
_pArr->iCount = 0;
_pArr->iMaxCount = 0;
}
main.cpp
#include "Arr.h"
#include <cstdlib>
#include <stdio.h>
// 가변 배열 : 데이터를 계속 확장해가면서 넣는 형태의 자료구조
int main()
{
tArr s1 = {};
// 배열 초기화
InitArr(&s1);
// 재할당
Reallocate(&s1);
// 데이터 추가
for (int i = 0; i < 10; ++i)
{
PushBack(&s1, i); // s1에 0~9까지 넣어라.
}
for (int i = 0; i < s1.iCount; ++i)
{
printf("%d\n", s1.pInt[i]); // i번째 데이터 출력
}
// 배열 메모리 해제
ReleaseArr(&s1);
return 0;
}
분할 구현 이용해서 가변 배열 자료형 만들기
Arr.h
#pragma once
// 동적 배열을 정의하는 구조체
typedef struct _tagArr {
int* pInt; // 동적 배열을 저장할 포인터
int iCount; // 배열에 현재 저장된 데이터 개수
int iMaxCount; // 배열의 최대 용량
} tArr;
// 배열 초기화
void InitArr(tArr* _pArr);
// 재할당
void Reallocate(tArr* _pArr, int newMax);
// 데이터 추가
void PushBack(tArr* _pArr, int value);
// 메모리 해제
void ReleaseArr(tArr* _pArr);
Arr.cpp
#include "Arr.h"
#include <iostream>
// 배열 초기화
void InitArr(tArr* _pArr) {
_pArr->pInt = new int[2]; // 2개의 정수를 저장할 메모리 할당 (new를 사용)
_pArr->iCount = 0; // 처음에는 데이터가 없으므로 iCount는 0
_pArr->iMaxCount = 2; // 배열의 용량은 2로 초기화
}
// 재할당
void Reallocate(tArr* _pArr, int newMax) {
int* newArray = new int[newMax]; // 새로운 배열을 할당
for (int i = 0; i < _pArr->iCount; i++) {
newArray[i] = _pArr->pInt[i]; // 기존 데이터를 새로운 배열로 복사
}
delete[] _pArr->pInt; // 기존 배열의 메모리 해제
_pArr->pInt = newArray; // 새로운 배열로 포인터 변경
_pArr->iMaxCount = newMax; // 최대 용량 업데이트
}
// 데이터 추가
void PushBack(tArr* _pArr, int value) {
if (_pArr->iCount == _pArr->iMaxCount) { // 배열이 가득 찼으면
Reallocate(_pArr, _pArr->iMaxCount * 2); // 배열 크기를 두 배로 늘린다.
}
_pArr->pInt[_pArr->iCount] = value; // 새로운 값 추가
_pArr->iCount++; // 데이터 개수 증가
}
// 메모리 해제
void ReleaseArr(tArr* _pArr) {
delete[] _pArr->pInt; // 동적 메모리 해제 (delete[] 사용)
_pArr->pInt = nullptr; // 포인터를 nullptr로 설정하여 안전하게 만든다.
_pArr->iCount = 0; // 데이터 개수 초기화
_pArr->iMaxCount = 0; // 최대 용량 초기화
}
main.cpp
#include "Arr.h"
#include <iostream>
using namespace std;
int main() {
tArr s;
// 배열 초기화
InitArr(&s);
// 배열에 데이터 추가
PushBack(&s, 10);
PushBack(&s, 20);
PushBack(&s, 30);
// 배열의 내용 출력
for (int i = 0; i < s.iCount; i++) {
cout << s.pInt[i] << endl;
}
// 메모리 해제
ReleaseArr(&s);
return 0;
}
▽ 출력 결과
10
20
30
'Programming Language > C++' 카테고리의 다른 글
[C++] 접근 지정자 (0) | 2024.04.01 |
---|---|
[C++] 래퍼런스 (vs. 포인터) (0) | 2024.04.01 |
[C++] wchar_t 배열 함수 - wcslen(), wcscat_s(), wcscmp(), wcscpy_s() (0) | 2024.03.27 |
[C++] 문자열 함수 - strlen(), strcat_s(), strcmp(), strcpy_s() (0) | 2024.03.27 |
[C++] 문자열 (0) | 2024.03.26 |