프로그래밍 / C++ / 언리얼

Programming/C | C++ | Unreal

[C++] 자료구조 - 배열(Array)

아트성 2022. 6. 15. 23:22

배열의 개요   

 

배열(Array)은 같은 타입으로 된 여러 개의 객체를 한 번에 다루고자 할 때 사용한다. 아래 그림처럼 메모리 상에서 배열은 각 원소가 연속적으로 배치된다. 

 

 

 

int grade1;
int grade2;
int grade3;
// ...
int grade30;

각 반마다 학생 수를 담는 변수를 선언한다고 해보자.  딱 봐도 뭔가 좋지 않다. 만일 학생 수가 30명이 아니라 100명이라면? 1000명이라면? 코드를 읽기도 매우 힘들어질 것이고, 작성하는 것도 쉽지 않을 것이다.

 

 

위의 예시를 배열로 바꿔보면 아래와 같다. 훨씬 더 간결해지고 보다 직관적으로 표현이 가능해진것이다.

int grades[30];

 

 

 

배열의 초기화   

 

  •  배열의 초기화는 { }을 사용한다.

  •  모든 원소에 대해 초기값을 지정하지 않을 시 자동으로 0이 된다.

  •  배열의 크기를 지정해주지 않고, 초기화를 했을 때는 자동으로 크기를 계산한다.

  •  메모리 상에서 배열은 각 원소가 연속적으로 배치되며, 데이터 영역에 할당이 된다. 

                                                          (동적할당을 통해서 힙 영역에 할당시킬 수 있다.)

 

  •  원소에 접근하려면 [ ] 연산자를 사용하면 된다.

  •  [ ] 연산자에 기입하는 숫자를 인덱스(Index)라고 하며, 0부터 시작함에 주의해야 한다.

// 1차원 배열
int arr[30]; 

int arr[5] = { 1, 2, 3 };

int arr[] = { 1, 2, 3 };


// 2차원 배열
int arr2[5][5];

int arr2[5][5] = {
    { 1, 2, 3, 4, 5 },
    { 6, 7, 8, 9, 10 }
};


// 3차원 배열
int arr5[2][2][2] = {
    {
        { 1, 2 },
        { 3, 4 },
        { 5, 6 },
    },
    {
        { 1, 2 },
        { 3, 4 },
        { 5, 6 },
    },
    {
        { 1, 2 },
        { 3, 4 },
        { 5, 6 },
    }
};

 

 

 

배열의 동적할당   

 

동적할당은 특성상 프로그램 실행도중(런타임)에서 힙 영역에 할당되는 특징이 있다.

또한 배열에 있어서 동적할당은 우리가 필요한 공간만큼 메모리 낭비없이 할당할 수 있다는 장점이 있다.

 

힙이라고 하면 약간 생소하게 들릴 수 있는데 쉽게 설명하자면, 힙에 할당되는 과정들을 식당 예약에 비유 할 수있다. 사람을 데이터 또는 객체, 식당테이블을 메모리 공간이라고 생각해보자. 사람은 테이블이 얼마나 필요한지 연락을 통해서 식당에 알려줄 수 있다. 이로써 식당에서는 얼마만큼의 테이블이 필요한지 파악할 수 있게되고 착오없이 사람이 앉을 공간을 마련시킬 수 있게된다.

 

동적할당 선언

1차원 배열 동적할당 / C++

int *arr = new int[X]; // 생성

delete[] arr; // 해제

 

2차원 배열 동적할당 / C++

// 생성
int** arr = new int* [Y];

for (int i = 0; i < Y; ++i)
{
    arr[i] = new int[X];
}

// 소멸
for (int i = 0; i < Y; ++i)
{
    delete[] arr[i];
}

delete[] arr;

 

 

1차원 배열 동적할당 / C

int *arr = (int*)malloc(sizeof(int)*X); // 생성

free arr; // 해제

 

2차원 배열 동적할당 / C

// 생성
int **arr = (int**)malloc(sizeof(int*)*X); // X = 입력받은 행의 size

for(int i = 0; i < X; i++)
{
	arr[i] = (int*)malloc(sizeof(int)*Y); // Y = 입력받은 열의 size
}

// 해제
for(int i = 0; i < X; i++)
{
	free (arr[i]);
}

free arr;

 

 

동적할당 예제

다음은 루프 안에서 사용자가 행 갯수와 열의 갯수를 입력하는 즉시 실시간으로 배열의 원소를 출력해주는 예제이다.

원소는 10부터 1씩 더해져서 출력이 된다.

#include <iostream>

#include <string.h>// memset 함수 사용

using namespace std;
int main()
{
    int X, Y;
    while (1)
    {
        cout << "동적할당 // 행의 개수를 입력시오 :_"; cin >> Y;
        cout << "동적할당 // 열의 개수를 입력시오 :_"; cin >> X;
        cout << "\n";

        int** arr = new int* [Y];
        for (int i = 0; i < Y; ++i)
        {
            arr[i] = new int[X];
            memset(arr[i], 0, sizeof(int) * X);   // 메모리 공간을 0으로 초기화
        }

        int count = 10;
        for (int i = 0; i < Y; i++)
        {
            for (int j = 0; j < X; j++)
            {
                arr[i][j] = count++;  // 배열에 원소 담기
            }
        }

        system("cls");

        for (int i = 0; i < Y; i++)
        {
            for (int j = 0; j < X; j++)
            {
                cout << arr[i][j] << ", "; // 배열의 원소 출력
            }
            cout << "\n";
        }

        for (int i = 0; i < Y; ++i)
        {
            delete[] arr[i]; // 각 행의 배열 제거
        }

        delete[] arr; // 전체 배열 제거
    }

}

 

 

 

 

배열을 매개변수로 받는 함수   

 

함수를 선언하고 배열을 매개변수로 받을때 두가지가 있는데 대괄호 형식으로 받는경우와 포인터로 받는경우가 있다. 

 

  1차원 배열 매개변수 타입

     int arr[  ],   int *arr

 

  2차원 배열 매개변수 타입

     int arr[  ][ 한 행의 원소갯수 ] ,   int (*arr)[ 한 행의 원소갯수 ]

 

함수 선언

// 1차원 배열
int ReturnValue(int arr[]) { } // 반환값자료형 함수이름(자료형 매개변수[])

int ReturnValue(int *arr) { } // 반환값자료형 함수이름(자료형 *매개변수)


// 2차원 배열
int ReturnValue(int arr[][한 행의 원소갯수]) { } // 반환값자료형 함수이름(자료형 매개변수[][한 행의 원소갯수])

int ReturnValue(int (*arr)[한 행의 원소갯수]) { } // 반환값자료형 함수이름(자료형 *매개변수[한 행의 원소갯수])

// 2차원 배열 (동적할당된 2차원 배열을 매개변수로 받는 함수)
int ReturnValue(int **arr){ } // 반환값자료형 함수이름 (자료형 **매개변수)

 

 

함수 정의 / 출력

#include <iostream>

#define arrSize 5
#define X 3
#define Y 2

using namespace std;

void PrintSort(int* arr, int size)
{
	arr[3] = 777;
	for (int i = 0; i < size; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << "\n";
}

void PrintSort2(int (*arr)[X], int x, int y)
{
	for (int i = 0; i < y; i++)
	{
		for (int j = 0; j < x; j++)
		{
			cout << arr[i][j] << " ";
		}
		cout << "\n";
	}
	cout << "\n";
}

int main()
{
	int arr[arrSize] = { 1, 2, 3, 4, 5}; 
	int arr2[Y][X] = {
		{1, 2, 3},
		{4, 5, 6}
	};

	cout << "[1차원 배열 출력]" << endl;
	PrintSort(arr, arrSize);	cout << "\n"; // result : 1, 2, 3, 777, 5

	cout << "[2차원 배열 출력]" << endl;
	PrintSort2(arr2, X, Y);		cout << "\n"; /* result: 1, 2, 3
														 4, 5, 6       */

	return 0;
}

 

콘솔창 출력

 

배열주소를 매개변수로 받는 함수, 처리과정

코드상에서 arr[3]은 *(arr + 3)과 의미가 같다. arr[3] = 777 | *(arr + 3) = 777; 식이 가능한 이유는 매개변수를 메모리주소(포인터)로 받아서 주소연산이 가능하게 되었기 때문이다. 

 

이처럼 배열의 메모리주소를 함수의 매개변수로 전달하기만하면, 배열의 메모리에 직접 접근이 가능하다.

또한 함수호출이 끝나면 보통 함수 내부에 있는 변수들은 해제가 일어나지만, 배열로 받게되면 함수호출이 끝나더라도 배열 안에있는 원소들을 바뀐 값으로 계속 유지시킬 수 있다.

 

따라서 4번째 칸인 0x13의 주소안의 값은 777 그대로 남게 된다.

 

 

 

 

 

배열을 반환하는 함수   

 

함수를 선언하고 반환 타입을 1차열 배열형식 또는 2차원 배열형식으로 하고싶을 때가 있다.

그럴때에는 아래와 같이 함수를 만들어 주면 된다. 2차원 배열은 특성상 가독성을 위해 typedef 키워드를 이용한다.  // typedef는 자료형을 원하는대로 바꿀 수 있다는 장점이 있다.

 

  1차원 배열 리턴함수

     int *PrintSort( ){ }

 

  2차월 배열 리턴함수

     typedef int(*Array2D)[한 행의 원소갯수];

     Array2D PrintSort2( ){ }

 

함수 선언

// 1차원 배열 반환 함수
int *PrintSort(){}  // 자료형 *함수이름(매개변수)


// 2차원 배열 반환 함수
typedef int(*Array2D)[한 행의 원소갯수]; //typedef 자료형 (*함수이름)[한 행의 원소갯수]
Array2D PrintSort2(){} // 자료형 함수이름(매개변수)

 

 

함수 정의 / 출력

#include <iostream>

#define arrSize 10
#define X 3
#define Y 2

using namespace std;

int *PrintSort(int *arr, int size)
{
	for (int i = 0; i < size; ++i) {
		arr[i] = 7 * (i + 1);
	}
	return arr;
}

typedef int(*Array2D)[X];

Array2D PrintSort2(int(*arr)[X], int x, int y)
{
	int count = 1;
	for (int i = 0; i < y; i++)
	{
		for (int j = 0; j < x; j++)
		{
			arr[i][j] = count++;
		}
	}
	return arr;
}

int main()
{
	// 1차원 배열
	cout << "[1차원 배열 출력]" << endl;
	int arr[arrSize] = { 1, 2, 3, 4, 5}; 
	int* arrPtr = PrintSort(arr, arrSize);

	for (int i = 0; i < arrSize; ++i) {
		cout << arrPtr[i] << " ";
	}
	cout << "\n\n";



	// 2차원 배열
	cout << "[2차원 배열 출력]" << endl;
	int arr2[Y][X];

	int (*arrPtr2)[X] = PrintSort2(arr2, X, Y);

	for (int i = 0; i < Y; i++)
	{
		for (int j = 0; j < X; j++)
		{
			cout << arrPtr2[i][j] << " ";
		}
		cout << "\n";
	}
	cout << "\n";

	return 0;
}



 

반응형