본문 바로가기
Computer Science/C++

[C++] 배열과 포인터(1차원 배열,다차원 배열)

by BrickSky 2023. 11. 5.

1) 1차원 배열


배열이란?

배열(arrary)은 같은 타입의 변수들로 이루어진 유한 집합이다.
배열을 구성하는 각각의 값을 배열 요소(element)라고 하며, 배열에서의 위치를 가리키는 숫자를 인덱스( index)라고 한다. C++에서 인덱스는 언제나 0부터 시작하며, 0을 포함한 양의 정수만을 가질 수 있다.
배열은 같은 종류의 데이터를 많이 다뤄야 하는 경우에 사용할 수 있는 가장 기본적인 자료구조이다.
배열은 선언되는 형식에 따라 1차원 배열, 2차원 배열뿐만 아니라 그 이상의 다차원 배열로도 선언할 수 있다.
 

1차원 배열

타입 배열이름[배열길이];

타입은 배열 요소로 들어가는 변수의 타입을 명시한다.
배열의 이름은 배열이 선언된 이후 배열에 접근할 때 사용되며 배열의 길이는 해당 배열이 몇 개의 요소를 갖게 되는지 명시한다.
 

int sum = 0;
int grade[3]; // 길이가 3인 int형 배열 선언

// 인덱스를 이용한 배열의 초기화
grade[0] = 85; // 국어 점수
grade[1] = 65; // 영어 점수
grade[2] = 90; // 수학 점수 

for (int i = 0; i < 3; i++)
{
    sum += grade[i]; // 인덱스를 이용한 배열로의 접근
}

cout << "국영수 과목 총 점수 합계는 " << sum << "점이고, 평균 점수는 " << (double)sum/3 <<"점입니다.";
국영수 과목 총 점수 합계는 240점이고, 평균 점수는 80점입니다.

위의 예제에서는 int형 데이터 3개를 저장할 수 있는 배열을 선언했다.
0부터 시작하는 인덱스를 활용해서 각각의 배열의 요소에 접근할 수 있다.

그림에서 알 수 있듯이! 배열의 이름은 배열의 첫 번째 요소와 같은 주소를 가리키는 것이다.
 

배열의 선언과 동시에 초기화하는 방법

C++에서는 변수와 마찬가지로 배열도 선언과 동시에 초기화할 수 있다.
괄호{} 를 사용해 초깃값을 나열하는 것을 초기화 리스트라고 한다.

타입 배열이름[배열길이] = {배열요소1, 배열요소2, ...};

단! 초기화 리스트의 타입과 배열의 타입은 반드시 일치해야 한다.
초기화 리스트의 개수가 배열의 총 길이보다 적다면 배열의 앞에서부터 차례대로 초기화된다.
이때 초기화되지 못한 나머지 배열 요소는 모두 0으로 초기화된다.
 
하지만 주의할 점은! 초기화 리스트를 이용한 초기화 방식은 반드시 배열의 선언과 함께 정의되어야 한다.
즉 배열이 먼저 선언된 후에는 이 방식으로 배열의 요소를 초기화 할 수 없는 것이다.

int arr1[3] = {0, 1, 2}; // 배열의 선언과 동시에 초기화는 가능함.
int arr2[3];             // 배열의 선언
arr2[3] = {0, 1, 2};     // 배열이 먼저 선언된 후에는 이 방식으로 초기화될 수 없음. 오류가 발생함.
arr2 = arr1;             // 길이가 같더라도 하나의 배열을 다른 배열에 통째로 대입할 수는 없음. 오류가 발생함.

 
아래의 코드는 배열이 선언된 이후 배열을 초기화하는 순서가 아닌, 배열을 선언과 동시에 초기화하는 리스트로 초기화하는 예제이다.

int sum = 0;
int grade[3] = {85, 65, 90}; // 길이가 3int형 배열의 선언과 동시에 초기화

for (int i = 0; i < 3; i++)

{
    sum += grade[i]; // 인덱스를 이용한 배열의 접근
}

cout << "국영수 과목 총 점수 합계는 " << sum << "점이고, 평균 점수는 " << (double)sum/3 <<"점입니다.";
국영수 과목 총 점수 합계는 240점이고, 평균 점수는 80점입니다.

 

배열의 길이 자동 설정

C++에서는 초기화 리스트에 맞춰 자동으로 배열의 길이를 설정할 수 있다.

타입 배열이름[] = {배열요소1, 배열요소2, ...};

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

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

해당 예제에서는 배열의 길이가 3으로 설정됨과 동시에 초기화 리스트에 의해 초기화된다.
 

배열의 특징

  • 배열의 길이를 선언할 때는 반드시 상수를 사용해야 한다.
  • 배열 요소의 인덱스는 언제나 0부터 시작한다.
  • C++ 컴파일러는 배열의 길이를 신경쓰지 않는다.
int sum = 0;
int grade[3] = {85, 65, 90}; // grade[0], grade[1], grade[2]만 선언 및 초기화
grade[3] = 100;              // grade[3]를 선언하지 않고 초기화 진행

for (int i = 0; i < 4; i++)  // grade[3]도 수식에 포함
{
    sum += grade[i];         // 인덱스를 이용한 배열의 접근
}
 
cout << "국영수 과목 총 점수 합계는 " << sum << "점이고, 평균 점수는 " << (double)sum/3 <<"점입니다.";
국영수 과목 총 점수 합계는 340점이고, 평균 점수는 113.333점입니다.

위의 예제의 경우 길이가 3인 int형 배열인 grade를 선언했다.
원래 배열 grade의 배열 요소는 grade[0], grade[1], grade[2] 이렇게만 이루어지는데 grade[3]이라는 요소가 추가되었다. 따라서 전체 grade[]의 배열 요소는 4개가 된다.
 

배열이 차지하는 메모리의 크기

sizeof 연산자를 통해 구할 수 있다.
아래의 경우 배열이 차지하는 총 메모리의 크기를 구하는 수식이다.

배열이 차지하는 메모리의 크기 = 배열의 길이 X sizeof(타입)

 
아래의 경우 배열의 길이를 구하는 수식이다.

배열의 길이 = sizeof(배열 이름) / sizeof(배열 이름[0])
int grade[] = {85, 65, 90};                 // 배열의 길이를 명시하지 않음
int len = sizeof(grade) / sizeof(grade[0]); // 배열의 길이를 구하는 공식

cout << "배열 grade의 길이는 " << len << "입니다.";
배열 grade의 길이는 3입니다.

 

C++에서의 배열 초기화

  • 배열을 초기화할 때는 대입연산자=을 사용하지 않아도 된다.
  • 값을 명시하지 않고 괄호 {}만을 사용하여 초기화하는 경우 모든 배열 요소는 0으로 초기화된다.
  • 초기화 리스트를 사용하여 배열을 초기화하는 경우에는 narrowing cast를 할 수 없다.

narrowing cast란 초기화를 통해 발생하는 암시적인 데이터 손실을 의미하는데.

int var = 3.14; // narrowing cast

위의 경우 int형 변수를 실숫값으로 초기화함으로 인해 데이터의 손실이 발생하게 된다.
이렇게 데이터의 손실이 발생하는 암시적인 타입 변환을 narrowing cast라고 하는 것이다.
 

int var = {3.14}; // 초기화 리스트를 통한 narrowing cast는 허용하지 않으므로, 경고를 발생시킴.

위에서 학습한 것처럼, 초기화 리스트를 사용하여 배열을 초기화하는 경우엔 narrowing cast가 불가능하기에 오류가 발생하는 것이다.
 
 

2) 다차원 배열


다차원배열

다차원 배열이란 2차원 이상의 배열을 의미한다. 배열의 요소로 또 다른 배열을 갖는 것이다.
 

2차원배열

2차원 배열이란 배열의 요소로 1차원 배열을 갖는 것이다.

타입 배열이름[행의길이][열의길이];

타입은 배열 요소로 저장되는 변수의 타입을 설정하며, 배열 이름은 배열이 선언된 후에 배열에 접근하기 위해 사용된다.

위의 이미지에 나온 대로 코드에 옮기면 다음과 같다.

int arr1[6] = {10, 20, 30, 40, 50, 60};
int arr2[2][3] = {10, 20, 30, 40, 50, 60};

arr1의 배열 요소의 값은 10, 20, 30, 40, 50, 60이고
arr2의 배열 요소의 값은 마찬가지로 10, 20, 30, 40, 50, 60이다.
 

배열의 선언과 동시에 초기화되는 방법

1차원 배열과 마찬가지로 2차원 배열도 선언과 동시에 초기화를 할 수 있다.
2차원 배열은 1차원 배열의 초기화 형태를 따르는 방식으로 초기화할 수도 있고, 배열의 모든 요소 혹은 일부 요소만을 초기화할 수도 있다.
 

1차원 배열의 초기화 형태를 따르는 방식

타입 배열이름[행의길이][열의길이] = {배열요소[0][0], 배열요소[0][1], ..., 배열요소[1][0], 배열요소[1][1], ..., 배열요소[2][0], 배열요소[2][1], ...};

이런 방식으로 초기화하면 [0][0]부터 초기화되며 초기화하는 배열 요소의 개수가 배열의 총 길이보다 작으면 나머지 배열 요소는 0으로 초기화된다.
 

배열의 모든 요소를 초기화하는 방식

타입 배열이름[행의길이][열의길이] =
{
    {배열요소[0][0], 배열요소[0][1], ...},
    {배열요소[1][0], 배열요소[1][1], ...},
    {배열요소[2][0], 배열요소[2][1], ...},
    ...
};

위의 방식과 크게 다르진 않지만 모든 요소를 초기화한다는 특징을 갖고 있다.
또한 직관적으로 2차원 배열의 모습을 볼 수 있으므로 해당 방식을 주로 사용한다.
 

배열의 일부 요소만을 초기화하는 방식

일부 요소만을 초기화할 수 있으며 초기화하지 않은 배열 요소는 0으로 초기화된다.

int arr_col_len, arr_row_len;

int arr[3][4] = {
    {10, 20},
    {30, 40, 50, 60},
    {0, 0, 70, 80}
};

arr_col_len = sizeof(arr[0]) / sizeof(arr[0][0]);              // 2차원 배열의 열의 길이를 계산함
arr_row_len = (sizeof(arr) / arr_col_len) / sizeof(arr[0][0]); // 2차원 배열의 행의 길이를 계산함

cout << "arr의 배열 요소의 값" << endl;
for (int i = 0; i < arr_row_len; i++)
{
    for (int j = 0; j < arr_col_len; j++)
    {
        cout << setw(4) << arr[i][j];
    }
    cout << endl;
}
arr의 배열 요소의 값

  10  20   0   0
  30  40  50  60
   0   0  70  80

초기화 하지 않은 배열 요소의 경우 자동적으로 0으로 초기화된 것을 확인할 수 있다.
 

배열의 길이 자동 설정

1차원 배열과 마찬가지로 2차원 배열도 배열의 길이를 명시하지 않고, 자동으로 배열의 길이를 설정할 수 있다. 단! 행의 길이는 생략해도 괜찮지만, 열의 길이는 반드시 명시해야 한다.
아래의 예제는 행의 길이를 생략한 코드이다. 행의 길이를 생략한 경우이기에 어떠한 오류도 발생하지 않지만, 열의 길이를 생략할 경우 컴파일 오류가 발생할 것이다.

int arr_col_len, arr_row_len;

//int arr[3][] = {

int arr[][4] = {
    {10, 20},
    {30, 40, 50, 60},
    {0, 0, 70, 80}
};

arr_col_len = sizeof(arr[0]) / sizeof(arr[0][0]);                 // 2차원 배열의 열의 길이를 계산함
arr_row_len = (sizeof(arr) / arr_col_len) / sizeof(arr[0][0]); // 2차원 배열의 행의 길이를 계산함

cout << "arr의 배열 요소의 값" << endl;

for (int i = 0; i < arr_row_len; i++)
{
    for (int j = 0; j < arr_col_len; j++)
    {
        cout << setw(4) << arr[i][j];
    }

    cout << endl;
}