배열 (Array)

배열은 동일한 타입의 데이터가 여러 개 저장되어 있는 데이터 저장 장소이다.

하나의 값 만을 저장할 수 있는 변수가 단독 주택이라면 배열은 일종의 아파트라고 할 수 있다.

배열은 구성하는 각각의 항목을 배열 요소(Array Element)라 하고, 배열에서의 위치를 가리키는 숫자를 인덱스 Index 라고 한다. C++에서 인덱스는 0 부터 시작하며, 0 을 포함한 양의 정수만을 가질 수 있다.


배열의 선언

배열은 다음과 같이 선언한다.

Type ArrayName[ArraySize];

아래의 코드는 배열 이름이 arr 이고, 길이는 5인 int 형 배열을 선언한 것이다.

// 배열 선언
int arr[5];

Untitled


배열의 초기화

다음과 같은 방법으로 배열의 선언과 동시에 배열을 초기화 할 수 있다.

Type ArrayName[ArraySize] = { Element1, Element2, /// }
int arr[5] = { 1, 2, 3, 4, 5};

배열의 길이를 따로 입력하지 않으면, 초기화 리스트의 배열 요소 개수에 맞춰 자동으로 배열의 길이가 설정된다.

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

배열의 길이를 다 채울 만큼의 초기 값이 선언되지 않으면 나머지 요소들은 기본값으로 초기화 된다.

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

배열의 특징

배열은 다음과 같은 특징을 갖는다.

배열의 길이를 나타낼 때에는 반드시 양의 정수를 이용해야 한다. 아래의 코드는 모두 잘못된 배열 선언이다.

int arr[];      // 배열의 크기를 지정하여야 합니다.(초기화 하지 않는 경우)
int arr[size];  // 배열의 크기를 변수로 할 수 없습니다.
int arr[0];     // 배열의 크기를 0으로 할 수 없습니다.
int arr[-2];    // 배열의 크기를 음수로 할 수 없습니다.
int arr[4.5];   // 배열의 크기를 실수로 할 수 없습니다.

배열의 인덱스는 항상 0 부터 시작한다. 따라서 배열 요소에 접근할 때에는 인덱스의 범위를 주의해야 한다. 유효한 인덱스의 범위는 0 에서 ArraySize - 1 까지 이다.

즉, 아래의 코드에서 arr 이라는 배열의 첫 번째 배열 요소는 arr[0] 두 번째 배열 요소는 arr[1] 마지막 배열 요소는 arr[4] 이다.

#include <iostream>

int main(void)
{
    int arr[5] = { 1, 2, 3, 4, 5 };

    // 배열 요소의 인덱스는 항상 0 부터 시작합니다.
    for (int index = 0; index < 5; index++)
    {
        std::cout << "arr[" << index << "] = " << arr[index];
        std::cout << "  index : " << index << std::endl;
    }

    return 0;
}
arr[0] = 1  index : 0
arr[1] = 2  index : 1
arr[2] = 3  index : 2
arr[3] = 4  index : 3
arr[4] = 5  index : 4

배열 요소는 변수와 동일하다. 따라서 배열 요소에 값을 저장할 수 있고, 배열 요소에 저장된 값을 꺼내거나 변경할 수도 있다.

#include <iostream>

int main(void)
{
    int arr[5] = { 1, 2, 3, 4, 5 };

    // 첫 번째 배열 요소(1)를 10으로 변경
    arr[0] = 10;

    // 세 번째 배열 요소(3)를 꺼내 num에 대입
    int num = arr[2];

    std::cout << "num : " << num << std::endl << std::endl;

    for (int index = 0; index < 5; index++)
    {
        std::cout << "arr[" << index << "] : " << arr[index] << std::endl;
    }

    return 0;
}
num : 3

arr[0] : 10
arr[1] : 2
arr[2] : 3
arr[3] : 4
arr[4] : 5

배열은 메모리의 연속적인 공간에 저장되며 배열의 이름은 배열의 첫 번째 요소와 같은 주소를 가리킨다.

#include <iostream>

int main(void)
{
    int arr[5] = { 10, 20, 30, 40, 50 };

    std::cout << arr << std::endl;        // 배열 이름
    std::cout << &arr[0] << std::endl;    // 첫 번째 배열 요소 주소 (주소 연산자 & 이용)

    return 0;
}
000000CB295AF8D8
000000CB295AF8D8

배열 이름과 배열의 첫 번째 요소가 같은 주소를 가리키는 것을 확인할 수 있다. 이 주소를 이용해 배열이 메모리상에 어떻게 저장되어 있는지 확인해 보면

Untitled

위와 같이 연속적인 공간에 값들이 저장되어 있는 것을 확인할 수 있다. 주소 값은 16진수이며 int 형이 4 byte 크기를 가지므로 4 byte 씩 떨어져서 저장된 모습이다.


배열과 함수

함수의 매개 변수는 일종의 지역 변수이다. 만약 함수의 인수로 배열 요소나 배열을 이용해 함수를 호출하면 어떻게 될까?

배열 요소를 함수의 인수로 하였을 때

배열 요소를 인수로 하여 함수를 호출할 경우 복사본이 전달되므로 원본 값이 변화하지 않는다.

#include <iostream>

// 배열 요소를 인수로 사용할 때
void Plus_1(int num)
{
    // 매개변수 1증가 
    num++;
    std::cout << "매개변수 : " << num << std::endl;
}

int main(void)
{
    int arr[5] = { 1, 2, 3, 4, 5 };
    std::cout << "함수 호출 전 원본 : " << arr[2] << std::endl;

    // arr[2]값을 인수로 사용
    Plus_1(arr[2]);
    std::cout << "함수 호출 후 원본 : " << arr[2] << std::endl;

    return 0;
}
함수 호출 전 원본 : 3
매개변수 : 4
함수 호출 후 원본 : 3

함수의 매개변수 값을 변화시켜도 원본 배열 요소의 값은 변화하지 않는다.

배열을 함수의 인수로 하였을 때

배열 요소를 함수의 인수로 하였을 때와 달리 배열을 함수의 인수로 할 경우 함수에서 매개변수의 값을 변화시킴에 따라 원본 배열의 값 또한 변화한다.

#include <iostream>

// 배열을 인수로 사용할 때
void Plus_1(int num[])
{
    // 매개변수 1증가 
    for (int i = 0; i < 5; i++)
    {
        num[i]++;
        std::cout << "매개변수 : " << num[i] << std::endl;
    }
}

int main(void)
{
    int arr[5] = { 1, 2, 3, 4, 5 };
    std::cout << "함수 호출 전 원본" << std::endl;
    for (int i = 0; i < 5; i++)
        std::cout << "원본 : " << arr[i] << std::endl;

    std::cout << std::endl;
    // arr을 인수로 사용
    Plus_1(arr);

    std::cout << std::endl << "함수 호출 후 원본" << std::endl;
    for (int i = 0; i < 5; i++)
        std::cout << "원본 : " << arr[i] << std::endl;

    return 0;
}
함수 호출 전 원본
원본 : 1
원본 : 2
원본 : 3
원본 : 4
원본 : 5

매개변수 : 2
매개변수 : 3
매개변수 : 4
매개변수 : 5
매개변수 : 6

함수 호출 후 원본
원본 : 2
원본 : 3
원본 : 4
원본 : 5
원본 : 6

이처럼 배열이 함수의 인수인 경우에는 배열의 원본(주소값)이 매개 변수를 통해 전달된다.

즉 배열은 매개 변수를 통해 원본은 참조하는 것이기 때문에 함수 인수로서의 사용에 주의해야 한다.

원본 배열의 변경을 금지하는 방법

const 지정자를 배열 매개 변수 앞에 붙여 원본 배열의 변경을 금지할 수 있다. const 지정자가 붙은 배열 매개 변수는 배열 요소의 변경이 불가능해진다.

#include <iostream>

// 배열 매개 변수 앞에 const 지정자
void Plus_1(const int num[])
{
    // 매개변수 1증가 
    for (int i = 0; i < 5; i++)
    {
        num[i]++; // 컴파일 오류 발생
        std::cout << "매개변수 : " << num[i] << std::endl;
    }
}

int main(void)
{
    int arr[5] = { 1, 2, 3, 4, 5 };

    // arr을 인수로 사용
    Plus_1(arr);

    for (int i = 0; i < 5; i++)
        std::cout << "원본 : " << arr[i] << std::endl;

    return 0;
}

Untitled

const 지정자를 배열 매개 변수 앞에 붙이면 그 배열의 내용이 변경될 경우 컴파일 오류가 발생한다.


다차원 배열

다차원 배열이란 배열 요소로 또 다른 배열을 가지는 배열로 2차원 이상의 배열을 다차원 배열이라고 한다.

배열이 다차원이 되면 필요한 메모리의 양은 급격하게 늘어나게 되므로 주의해야 한다. 특별한 경우를 제외하고 일반적으로는 3차원 이상의 다차원 배열은 피하는 것이 좋다.

2차원 배열