Skip to content

29. 배열(Array)


1. 키워드

  • 배열(Array)
  • 요소(Element)


2. 배열 사용하기

  • 배열은 같은 자료형의 변수를 일렬로 늘어놓은 형태이며 반복문과 결합하면 연속적이고 반복되는 값을 손쉽게 처리할 수 있다.


3. 배열을 선언하고 요소에 접근하기

  • 배열은 변수 이름 뒤에 [](대괄호)를 붙인 뒤 크기를 설정한다.
  • 그리고 배열을 선언하면서 값을 초기화할 때는 {}(중괄호)를 사용한다.


자료형 배열이름[크기];
자료형 배열이름[크기] = {, , };
#include <stdio.h>

int main()
{
    int numArr[10] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110}; // 배열을 생성하고 값 할당

    printf("%d\n", numArr[0]); // 배열의 첫 번째(인덱스 0) 요소 출력
    printf("%d\n", numArr[5]); // 배열의 여섯 번째(인덱스 5) 요소 출력
    printf("%d\n", numArr[9]); // 배열의 열 번째(인덱스 9) 요소 출력

    return 0;
}

// 11
// 66
// 110


  • int numArr[10]은 크기가 10int 타입 배열을 선언한다는 뜻이다.
  • 배열을 선언하면서 값을 초기화할 때는 {} 안의 값 개수는 배열의 크기보다 작아도 되지만 크면 안 된다.
  • 또한, {}를 사용하여 배열에 값을 할당하는 방법은 배열을 선언할 때만 사용할 수 있으며 이미 선언된 배열에는 사용할 수 없다.


  • 배열에 값이 저장된 공간을 요소라고 한다.
  • 배열에서 각 요소에 접근하려면 배열 뒤에 []를 사용하며 [] 안에 각 요소의 인덱스를 지정해 주면 된다.


배열[인덱스]
numArr[0]; // 첫 번째 요소, 인덱스 0
numArr[5]; // 여섯 번째 요소, 인덱스 5
numArr[9]; // 열 번째 요소, 인덱스 9


  • 여기서 주의할 점은 배열의 인덱스는 항상 0부터 시작한다는 점이다.
  • 따라서 배열 numArr의 첫 번째 요소는 numArr[0]이 된다.


001


배열 선언과 배열 인덱스

  • 배열을 선언할 때도 []를 사용하고 배열의 요소에 접근할 때도 []를 사용한다.
  • 같은 [] 기호를 사용해서 헷갈리기 쉽지만 선언과 사용을 구분해서 생각하면 된다.
  • 즉, 배열을 선언할 때 []는 "이 변수가 배열이고 크기는 얼마다"라고 알려주는 역할이고, 배열을 사용할 때 []는 "배열의 요소에 접근하겠다"는 뜻이다.


  • 요소가 0개인 배열은 선언할 수 없다.
  • 따라서 배열을 선언할 때 크기는 항상 1 이상이다.
  • 하지만 배열에 접근할 때는 인덱스가 0부터 시작한다.


int numArr[0]; // 컴파일 에러. 크기가 0인 배열은 선언할 수 없음
int numArr[1]; // 크기가 1인 배열. 요소가 1개
numArr[0];     // 배열의 첫 번째 요소, 인덱스 0에 접근


  • 배열의 인덱스가 0부터 시작하는 이유는 메모리 주소가 0부터 시작하기 때문이다.
  • 배열도 포인터이므로 인덱스가 0부터 시작하면 요소 접근과 포인터 연산이 일치하게 된다.


배열을 선언할 때 크기 생략하기

  • 배열을 선언할 때 값을 초기화한다면 배열의 크기를 생략할 수 있다.
  • 초기화를 하지 않을 때는 생략할 수 없다.


자료형 배열이름[] = {, , };
int numArr1[] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110}; // 배열의 크기 생략

int numArr2[]; // 컴파일 에러


4. 배열을 0으로 초기화하기

  • 이번에는 배열의 모든 요소를 간단하게 0으로 초기화해 보자.


자료형 배열이름[크기] = {0,};
#include <stdio.h>

int main()
{
    int numArr[10] = { // 배열의 요소를 모두 0으로 초기화
        0,
    };

    printf("%d\n", numArr[0]); // 배열의 첫 번째(인덱스 0) 요소 출력
    printf("%d\n", numArr[5]); // 배열의 여섯 번째(인덱스 5) 요소 출력
    printf("%d\n", numArr[9]); // 배열의 열 번째(인덱스 9) 요소 출력

    return 0;
}

// 0
// 0
// 0


  • 배열을 선언할 때 {0,}을 할당하여 배열의 요소를 모두 0으로 초기화했다.
  • 이렇게 하면 초기화할 때 0을 일일이 나열하지 않아도 된다.


5. 배열의 요소에 값 할당하기

  • 배열은 []로 요소에 접근한 뒤 값을 할당할 수 있다.


배열[인덱스] = ;
#include <stdio.h>

int main()
{
    int numArr[10]; // 크기가 10인 배열 선언

    numArr[0] = 11;  // 인덱스가 0인 배열의 요소에 값 할당
    numArr[1] = 22;  // 인덱스가 1인 배열의 요소에 값 할당
    numArr[2] = 33;  // 인덱스가 2인 배열의 요소에 값 할당
    numArr[3] = 44;  // 인덱스가 3인 배열의 요소에 값 할당
    numArr[4] = 55;  // 인덱스가 4인 배열의 요소에 값 할당
    numArr[5] = 66;  // 인덱스가 5인 배열의 요소에 값 할당
    numArr[6] = 77;  // 인덱스가 6인 배열의 요소에 값 할당
    numArr[7] = 88;  // 인덱스가 7인 배열의 요소에 값 할당
    numArr[8] = 99;  // 인덱스가 8인 배열의 요소에 값 할당
    numArr[9] = 110; // 인덱스가 9인 배열의 요소에 값 할당

    printf("%d\n", numArr[0]); // 배열의 첫 번째(인덱스 0) 요소 출력
    printf("%d\n", numArr[5]); // 배열의 여섯 번째(인덱스 5) 요소 출력
    printf("%d\n", numArr[9]); // 배열의 열 번째(인덱스 9) 요소 출력

    return 0;
}

// 11
// 66
// 110


  • 배열의 요소를 출력할 때와 마찬가지로 []에 인덱스를 지정한 뒤 값을 할당하면 된다.


  • 배열의 범위를 벗어난 인덱스에 접근하면 어떻게 되는지 확인해 보자.


#include <stdio.h>

int main()
{
    int numArr[10]; // 크기가 10인 배열 선언

    numArr[0] = 11;  // 인덱스가 0인 배열의 요소에 값 할당
    numArr[1] = 22;  // 인덱스가 1인 배열의 요소에 값 할당
    numArr[2] = 33;  // 인덱스가 2인 배열의 요소에 값 할당
    numArr[3] = 44;  // 인덱스가 3인 배열의 요소에 값 할당
    numArr[4] = 55;  // 인덱스가 4인 배열의 요소에 값 할당
    numArr[5] = 66;  // 인덱스가 5인 배열의 요소에 값 할당
    numArr[6] = 77;  // 인덱스가 6인 배열의 요소에 값 할당
    numArr[7] = 88;  // 인덱스가 7인 배열의 요소에 값 할당
    numArr[8] = 99;  // 인덱스가 8인 배열의 요소에 값 할당
    numArr[9] = 110; // 인덱스가 9인 배열의 요소에 값 할당

    printf("%d\n", numArr[-1]); // 음수이므로 잘못된 인덱스
    printf("%d\n", numArr[10]); // 배열의 범위를 벗어난 인덱스
    printf("%d\n", numArr[20]); // 배열의 범위를 벗어난 인덱스

    return 0;
}

// -858993460 (쓰레기 값)
// -858993460 (쓰레기 값)
// 13651968   (쓰레기 값)


  • 배열의 요소에 접근할 때 인덱스로 음수를 지정하거나, 배열의 크기를 벗어난 인덱스를 지정해도 컴파일 에러가 발생하지 않는다.
  • 하지만 실행을 해보면 쓰레기 값이 출력된다.
  • 즉, 배열의 범위를 벗어난 인덱스에 접근하면 배열이 아닌 다른 메모리 공간에 접근하게 된다.


6. 배열의 크기 구하기

  • 반복문으로 크기가 10인 배열의 요소를 출력한다면 다음과 같이 만들 수 있을 것이다.


int numArr[10] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110}; // 요소가 10개

for (int i = 0; i < 10; i++) // 0부터 10까지 반복
{
    // 요소에 접근
}


  • 코드를 다 작성했는데 배열의 크기를 15개로 늘려야 하는 상황이 발생했다.
  • 그래서 배열의 크기를 15로 늘렸지만 실수로 반복문의 조건식은 수정하지 못했다.


// 배열의 크기를 15로 늘림
int numArr[15] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110, 121, 132, 143, 154, 165};

for (int i = 0; i < 10; i++) // 실수로 조건식은 수정하지 못했음
{
    // 요소에 접근
}


  • 버그는 이런 과정으로 발생하게 된다.
  • 여기서는 수정할 부분이 두 곳이지만 반복문이 여러 개로 늘어나면 실수할 가능성은 그만큼 높아진다.
  • 실수를 방지하기 위해서는 배열의 크기가 바뀌었을 때 배열의 크기를 알아서 계산하도록 만들면 된다.
  • 이미 선언된 배열이 차지하는 전체 공간과 요소의 개수는 sizeof 연산자를 활용하면 간단하게 구할 수 있다.


#include <stdio.h>

int main()
{
    int numArr[10] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110}; // 크기가 10인 int 타입 배열

    printf("%d\n", sizeof(numArr));               // 4바이트 크기의 요소가 10이므로 40
    printf("%d\n", sizeof(numArr) / sizeof(int)); // 배열의 크기를 구할 때는
                                                  // 전체 공간을 요소의 크기로 나눠줌

    return 0;
}

// 40
// 10


  • 배열에 sizeof 연산자를 사용해 보면 배열이 차지하는 전체 공간이 출력된다.
  • 따라서 int numArr[10];은 크기가 4바이트인 int 타입 요소가 10개 모여있으므로 40이 출력된다.


sizeof(numArr); // 40


  • 배열의 크기(요소 개수)를 구할 때는 배열이 차지하는 전체 공간에서 요소의 크기로 나눠준다.


sizeof(numArr) / sizeof(int); // 40 / 4는 10, 요소의 개수는 10개
                              // sizeof(numArr) / sizeof(numArr[0]) 형태도 가능


배열의 크기와 인덱스

  • C에서는 인덱스가 배열의 범위를 벗어났는지 검사하지 않으므로 프로그래머가 항상 이 부분을 생가하면서 작성해야 한다.
  • 배열의 크기(요소 개수)를 구해놓고, 배열에 배열에 접근하기 전에 인덱스가 요소 개수 - 1을 넘지 않는지 확인하는 것도 좋은 방법이다.


int numArr[10];                           // 요소가 10개인 배열
int index = 10;                           // 배열의 범위를 벗어나는 인덱스
int count = sizeof(numArr) / sizeof(int); // 배열의 크기(요소의 개수)를 구함

if (index <= count - 1) // 인덱스가 count - 1보다 작거나 같으면 배열의 범위를 벗어나지 않았음
{
    numArr[index] = 999;
}


7. 반복문으로 배열의 요소를 모두 출력하기

  • 이번에는 반복문을 사용하여 배열의 요소를 모두 출력해 보자.


#include <stdio.h>

int main()
{
    int numArr[10] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110}; // 크기가 10인 int 타입 배열

    for (int i = 0; i < sizeof(numArr) / sizeof(int); i++) // 배열의 요소 개수만큼 반복
    {
        printf("%d\n", numArr[i]); // 배열의 인덱스에 반복문의 변수 i를 지정
    }

    return 0;
}

// 11
// 22
// 33
// 44
// 55
// 66
// 77
// 88
// 99
// 110


  • 반복문에서 for (int i = 0; i < sizeof(numArr) / sizeof(int); i++)처럼 조건식에 sizeof 연산자를 사용하여 배열의 요소 개수를 구한 뒤 반복했다.


for (int i = 0; i < sizeof(numArr) / sizeof(int); i++) // 배열의 요소 개수만큼 반복


  • 반복문의 변수 i는 변화식을 통해 1씩 증가하므로 배열의 인덱스에 i를 넣으면 배열의 요소를 순서대로 모두 접근할 수 있다.


for (int i = 0; i < sizeof(numArr) / sizeof(int); i++) // 배열의 요소 개수만큼 반복
{
    printf("%d\n", numArr[i]); // 배열의 인덱스에 반복문의 변수 i를 지정
}


  • 반복문으로 배열의 요소를 역순으로 출력할 수도 있다.


#include <stdio.h>

int main()
{
    int numArr[10] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110}; // 크기가 10인 int 타입 배열

    for (int i = sizeof(numArr) / sizeof(int) - 1; i >= 0; i--) // 요소 개수 - 1부터 역순으로 반복
    {
        printf("%d\n", numArr[i]); // 배열의 인덱스에 반복문의 변수 i를 지정
    }

    return 0;
}

// 110
// 99
// 88
// 77
// 66
// 55
// 44
// 33
// 22
// 11


  • 반복문으로 배열의 요소를 역순으로 출력할 때 주의할 점이 있다.
  • 초깃값에 배열의 크기(요소 개수)를 바로 넣어버리면 처음부터 배열의 인덱스를 벗어난 상태가 된다.


int numArr[10]; // 요소가 10개
numArr[10];     // 10을 지정하면 배열의 인덱스를 벗어난 상태가 됨
numArr[9];      // 마지막 요소의 인덱스는 요소의 개수 - 1


  • 배열의 인덱스는 0부터 시작하므로 마지막 요소의 인덱스는 요소의 개수에서 1을 빼줘야 한다.
  • 즉, 요소가 10개인 배열에서 마지막 요소의 인덱스는 9가 된다.
  • 따라서 다음과 같이 초기식에는 배열의 요소 개수를 구한 뒤 1을 빼주고, 조건식에는 i >= 0;을 지정하여 0까지 반복할 수 있도록 만든다.


for (int i = sizeof(numArr) / sizeof(int) - 1; i >= 0; i--) // 요소 개수 - 1부터 역순으로 반복


8. 배열의 요소 합계 구하기

  • 이번에는 배열에서 요소의 합을 구해 보자.


#include <stdio.h>

int main()
{
    int numArr[10] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110}; // 크기가 10인 int 타입 배열
    int sum = 0;                                                // 합을 저장할 변수는 0으로 초기화

    for (int i = 0; i < sizeof(numArr) / sizeof(int); i++) // 배열의 요소 개수만큼 반복
    {
        sum += numArr[i]; // sum과 배열의 요소를 더해서 다시 sum에 저장
    }

    printf("%d\n", sum);

    return 0;
}

// 605


  • 배열의 요소 개수만큼 반복하면서 변수 sum과 요소를 더한 뒤 다시 sum에 저장해 준다.
  • 즉, 요소의 값을 sum에 누적시키는 것이다.


  • += 연산자를 풀어서 쓰면 다음과 같다.


sum = sum + numArr[i];


  • 여기서 변수 sum0으로 초기화해 주지 않으면 쓰레기 값이 들어있게 된다.
  • 그래서 쓰레기 값과 요소의 값을 더하게 되므로 잘못된 결과가 나온다.
  • 값을 누적시킬 때는 변수를 반드시 0으로 초기화해야 한다.


9. 배열의 요소에 저장된 값을 두 배로 만들기

  • 이번에는 배열의 요소에 저장된 값을 두 배로 만들어보자.


#include <stdio.h>

int main()
{
    int numArr[10] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110}; // 크기가 10인 int 타입 배열

    for (int i = 0; i < sizeof(numArr) / sizeof(int); i++) // 배열의 요소 개수만큼 반복
    {
        numArr[i] *= 2; // 배열의 요소에 2를 곱해서 다시 요소에 저장
    }

    for (int i = 0; i < sizeof(numArr) / siof(int); i++) // 배열의 요소 개수만큼 반복
    {
        printf("%d\n", numArr[i]);
    }

    return 0;
}

// 22
// 44
// 66
// 88
// 110
// 132
// 154
// 176
// 198
// 220


  • 배열의 요소에 저장된 값을 두 배로 만들려면 배열의 요소에 접근하여 2로 곱한 뒤 다시 요소에 저장해 주면 된다.


  • *= 연산자를 풀어서 쓰면 다음과 같다.


numArr[i] = numArr[i] * 2;


  • 이처럼 배열은 반복문으로 반복하면서 배열의 요소에 저장된 값을 변경할 수 있다.


10. 배열을 포인터에 넣기

  • 배열은 사실 첫 번째 요소의 주솟값만 담고 있다.


002


  • 즉, 배열은 주솟값이기 때문에 포인터에 넣을 수 있다.
  • 따라서 다음과 같이 포인터에 배열을 넣은 뒤 포인터에서 인덱스로 요소에 접근할 수 있다.


#include <stdio.h>

int main()
{
    int numArr[10] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 110}; // 크기가 10인 int 타입 배열

    int *numPtr = numArr; // 포인터에 int 타입 배열을 할당

    printf("%d\n", *numPtr); // 배열의 주소가 들어있는 포인터를 역참조하면
                             // 배열의 첫 번째 요소에 접근

    printf("%d\n", *numArr); // 배열 자체를 역참조해도 배열의 첫 번째 요소에 접근

    printf("%d\n", numPtr[5]); // 배열의 주소가 들어있는 포인터는 인덱스로 접근할 수 있음

    printf("%d\n", sizeof(numArr)); // sizeof로 배열의 크기를 구하면
                                    // 배열이 메모리에 차지하는 공간이 출력됨

    printf("%d\n", sizeof(numPtr)); // sizeof로 배열의 주소가 들어있는 포인터의 크기를 구하면
                                    // 포인터의 크기가 출력됨

    return 0;
}

// 11
// 11
// 66
// 40
// 8


  • int *numPtr = numArr;처럼 배열을 포인터에 바로 할당할 수 있다.
  • 단, 자료형이 같아야 하며 1차원 배열이라면 *가 한 개인 단일 포인터이어야 한다.


  • 배열을 포인터에 할당한 뒤 포인터를 역참조해 보면 배열의 첫 번째 요소의 값이 나온다.
  • 마찬가지로 배열 자체도 역참조해 보면 배열의 첫 번째 요소의 값이 나온다.
  • 즉, 실제로는 배열도 포인터라 할 수 있다.


printf("%d\n", *numPtr); // 배열의 주소가 들어있는 포인터를 역참조하면
                         // 배열의 첫 번째 요소에 접근

printf("%d\n", *numArr); // 배열 자체를 역참조해도 배열의 첫 번째 요소에 접근


  • 배열의 주소가 들어있는 포인터는 인덱스를 통해 요소에 접근할 수 있다.


printf("%d\n", numPtr[5]); // 배열의 주소가 들어있는 포인터는 인덱스로 접근할 수 있음


  • 배열과 포인터가 다른 점은 sizeof로 크기를 계산했을 때이다.
  • 배열에 sizeof 연산자를 사용하면 배열이 차지하는 전체 공간이 출력되지만 sizeof로 배열의 주소가 들어있는 포인터의 크기를 구해 보면 그냥 포인터의 크기만 나온다.


printf("%d\n", sizeof(numArr)); // sizeof로 배열의 크기를 구하면
                                // 배열이 메모리에 차지하는 공간이 출력됨

printf("%d\n", sizeof(numPtr)); // sizeof로 배열의 주소가 들어있는 포인터의 크기를 구하면
                                // 포인터의 크기가 출력됨


11. 배열을 활용하여 10진수를 2진수로 변환하기

  • 이번에는 배열을 응용해서 10진수를 2진수로 변환한 뒤 배열에 넣어보자.
  • 참고로 10진수를 0이 될 때까지 2로 계속 나눈 뒤 나머지를 역순으로 읽으면 2진수가 된다.


  • 예를 들어 13을 2진수로 변환한다면, 132로 나누면 몫은 6이 나오고 나머지는 1이 나온다.
  • 613 아래에 적고 16 옆에 적는다.
  • 이런 식으로 계속 2로 나눠서 몫과 나머지를 구해 몫이 0이 나오면 가장 아래의 나머지부터 역순으로 읽는다.


003


  • 이제 위의 식을 코드로 표현해 보자.


#include <stdio.h>

int main()
{
    int decimal = 13;
    int binary[20] = {
        0,
    };

    int position = 0;

    while (1)
    {
        binary[position] = decimal % 2; // 2로 나누었을 때 나머지를 배열에 저장
        decimal = decimal / 2;          // 2로 나눈 몫을 저장

        position++; // 자릿수 변경

        if (decimal == 0) // 몫이 0이 되면 반복을 끝냄
            break;
    }

    // 배열의 요소를 역순으로 출력
    for (int i = position - 1; i >= 0; i--)
    {
        printf("%d", binary[i]);
    }

    printf("\n");

    return 0;
}

// 1101


  • 먼저 변환할 10진수 13을 변수 decimal에 저장하고, 2진수를 저장할 배열 binary를 선언한 뒤 0으로 초기화했다.


int decimal = 13;
int binary[20] = {
    0,
};


  • 10진수를 2진수로 변환하는 식은 같은 계산을 계속 반복하는 구조이다.
  • 따라서 while로 무한 루프를 만들었다.
  • 반복문 안에서는 binary[position] = decimal % 2와 같이 10진수를 2로 나눴을 때 나머지를 구해 binary 배열에 넣어준다.
  • 그리고 decimal2로 나눈 몫을 다시 decimal에 저장해 준다.
  • 계산이 끝났다면 다음 자리를 저장할 수 있도록 position의 값을 증가시킨다.


int position = 0;

while (1)
{
    binary[position] = decimal % 2; // 2로 나누었을 때 나머지를 배열에 저장
    decimal = decimal / 2;          // 2로 나눈 몫을 저장

    position++; // 자릿수 변경


  • 계속 반복하다가 decimal1이 되면 12로 나눌 수가 없으므로 몫은 0이 된다.
  • 이때 break로 반복문을 종료한다.
    if (decimal == 0) // 몫이 0이 되면 반복을 끝냄
        break;
}


  • 가장 아래의 나머지부터 역순으로 읽어야 하므로 배열의 요소를 역순으로 출력해 준다.
  • 여기서 주의할 점은 배열의 인덱스가 0부터 시작하므로 position - 1부터 0까지 감소하도록 만들어야 한다.
  • 만약 position부터 반복하면 처음부터 배열을 벗어난 상태가 된다.


// 배열의 요소를 역순으로 출력
for (int i = position - 1; i >= 0; i--)
{
    printf("%d", binary[i]);
}

References