C, 배열


배열이란?


예를 들어 사용자에게 100개의 정수를 입력받는 프로그램이 있다고 가정하겠습니다.

그럼 배열을 사용하지 않는다면 변수를 100개를 정의해야합니다.

int a1, a2, a3, a4, a5, a6, a7, a8, …, a100;

뭔가 이런식으로 각각의 변수를 모두 정의하는건 비효율적으로 보입니다.

하지만 배열을 쓴다면 다음과 같이 간단하게 100개의 변수를 정의할 수 있습니다.

int a[100];




이번 챕터의 목표


1. 배열의 마지막 열/행을 해당 열/행의 요소의 합으로 채우기

c_array_goal1


2. 달팽이 배열 채우기

c_array_goal2




1차원 배열


먼저 간단한 1차원 배열의 예제코드를 살펴보겠습니다.

5개의 정수를 입력받고 이를 그대로 출력하는 프로그램입니다.

#include <stdio.h>

int main(void)
{
    printf("5개의 수를 입력받고, 그대로 출력하는 프로그램\n\n");

    // 크기가 5인 배열 정의
    int n[5] = {0};
    
    for(int i=0; i<5; i++){
        printf("%d번째 수 입력: ", i+1);
        scanf("%d", &n[i]);
    }
    
    putchar('\n');
    for(int i=0; i<5; i++){
        printf("%d번째 수: %d\n", i+1, n[i]);
    }
    
    putchar('\n');
    return 0;
}

/*
 5개의 수를 입력받고, 그대로 출력하는 프로그램
 
 1번째 수 입력: 5
 2번째 수 입력: 4
 3번째 수 입력: 3
 4번째 수 입력: 2
 5번째 수 입력: 1
 
 1번째 수: 5
 2번째 수: 4
 3번째 수: 3
 4번째 수: 2
 5번째 수: 1
*/

배열 정의한 부분을 눈여겨 보면 됩니다.

저런식으로 정의하면 5개의 변수인

n[0], n[1], n[2], n[3], n[4]의 값은 모두 0이 됩니다.

그리고 방금 쓴 것처럼 배열은 순서가 0부터 시작합니다.

그래서 크기가 5인 배열은 0번째부터 4번째까지 요소가 존재하게 됩니다.


그리고 배열의 이름은 0번째 요소의 주소입니다.

그걸 확인하는 예제를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int arr[5] = {0};
    printf("배열의 이름과 0번째 요소의 주소 출력해보기\n\n");
    
    printf("arr = %d\n", arr);
    printf("&arr[0] = %d\n", &arr[0]);
    printf("&arr[1] = %d\n\n", &arr[1]);
    
    char arr2[5] = {0};
    printf("arr2 = %d\n", arr2);
    printf("&arr2[0] = %d\n", &arr2[0]);
    printf("&arr2[1] = %d\n\n", &arr2[1]);
    
    return 0;
}

/*
 배열의 이름과 0번째 요소의 주소 출력해보기
 
 arr = -272632400
 &arr[0] = -272632400
 &arr[1] = -272632396
 
 arr2 = -272632409
 &arr2[0] = -272632409
 &arr2[1] = -272632408
*/

구체적인 배열의 주소는 컴퓨터 환경마다 다르게 나올 것입니다.

중요한 것은 배열의 이름과 해당 배열의 첫번째 요소의 주소는 같다는 것입니다.

또한 int배열인 arr에서는 arr[0]과 arr[1]의 주소차이가 4입니다.

char배열인 arr2에서는 arr2[0]과 arr2[1]의 주소차이가 1입니다.

바로 자료형의 크기 차이만큼인 걸 알 수 있습니다.

char는 1바이트, int는 4바이트 자료형이었죠.


이번엔 구체적으로 배열의 요소에 접근하여 값을 변경해보겠습니다.

#include <stdio.h>

int main(void)
{
    int arr[5] = {10, 20, 30, 40, 50};
    
    for(int i=0; i<5; i++) printf("n[%d] = %d\n", i, arr[i]);
    putchar('\n');
    
    arr[0] = 100;
    arr[4] = 500;
    
    printf("after change...\n");
    
    for(int i=0; i<5; i++) printf("n[%d] = %d\n", i, arr[i]);
    
    putchar('\n');
    return 0;
}

/*
 n[0] = 10
 n[1] = 20
 n[2] = 30
 n[3] = 40
 n[4] = 50
 
 after change...
 n[0] = 100
 n[1] = 20
 n[2] = 30
 n[3] = 40
 n[4] = 500
*/

코드를 살펴보면 배열의 요소에 접근하는 법을 알 수 있습니다.


간단하게 배열에서 가장 큰 값을 찾는 프로그램을 작성해보겠습니다.

#include <stdio.h>

int main(void)
{
    int arr[] = {5, -1, 7, 10, 3};
    int max = arr[0];
    
    printf("배열에서 최대값 찾는 프로그램\n\n");
    
    printf("배열 arr = {");
    for(int i=0; i<5; i++) printf("%d ", arr[i]);
    printf("}\n\n");
    
    for(int i=1; i<5; i++){
        if(max < arr[i]) max = arr[i];
    }
    printf("max: %d\n\n", max);
    
    return 0;
    
}

/*
 배열에서 최대값 찾는 프로그램
 
 배열 arr = {5 -1 7 10 3 }
 
 max: 10
*/


이번에는 주어진 배열을 버블소트 방식으로 오름차순 정렬해보겠습니다.

버블소트에 대한 설명은 여기를 참고하시면 됩니다.

#include <stdio.h>

int main(void)
{
    int arr[] = {30, 40, 10, 50, 20};
    int temp = 0;
    
    printf("unsorted numbers: ");
    for(int i=0; i<5; i++) printf("%d ", arr[i]);
    
    // bubble sort
    for(int i=0; i<4; i++){
        for(int j=0; j<4-i; j++){
            if(arr[j] > arr[j+1]){
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
    
    printf("\n\nsorted numbers: ");
    for(int i=0; i<5; i++) printf("%d ", arr[i]);
    printf("\n\n");
    
    return 0;
}

/*
 unsorted numbers: 30 40 10 50 20
 
 sorted numbers: 10 20 30 40 50

*/


이번에는 문자열에 대해 살펴보았습니다.

이전 챕터에서도 살펴봤듯이 문자열 또한 char자료형의 배열로 정의합니다.

다음은 다양하게 문자열을 정의해본 코드입니다.

#include <stdio.h>

int main(void)
{
    int intList[] = { 1, 2, 3, 4, 5 };
    
    // 다양한 문자열 정의 방법들
    char string1[6] = { 't', 'e', 'e', 'm', 'o', '\0' };
    char string2[6] = { "teemo" };
    char string3[10] = { "teemo" };
    char *string4 = "teemo";
    
    puts(string1);
    puts(string2);
    puts(string3);
    puts(string4);
    
    putchar('\n');
    printf("문자열과 배열 길이가 같을 때(string1) 정수로 출력\n");
    for (int i = 0; i < 6; i++) printf("%d ", string1[i]);
    printf("\n\n");
    
    printf("배열의 길이가 문자열의 길이보다 더 길때(string3) 정수로 출력\n");
    for (int i = 0; i < 10; i++) printf("%d ", string3[i]);
    printf("\n\n");
    
    printf("'\\0'을 정수로 출력하면 %d\n", '\0');
    
    if (0 == '\0') printf("0 == '\\0'\n");
    
    putchar('\n');
    
    return 0;
}

/*
 teemo
 teemo
 teemo
 teemo
 
 문자열과 배열 길이가 같을 때(string1) 정수로 출력
 116 101 101 109 111 0
 
 배열의 길이가 문자열의 길이보다 더 길때(string3) 정수로 출력
 116 101 101 109 111 0 0 0 0 0
 
 '\0'을 정수로 출력하면 0
 0 == '\0'
*/

문자열을 정의하는 방법이 4가지가 나와있습니다.

참고로 string4와 같이 정의하는 방법은 아직 살펴보지 않은

포인터 변수을 선언하는 방법입니다.

포인터 변수메모리의 주소를 저장하기 위한 전용변수입니다.

“teemo”라는 문자열 자체는 문자의 배열이고,

배열메모리의 주소로 식별하는데,

그 주소를 선언하는 포인터 변수의 초깃값으로 설정한 것입니다.

자세한 내용은 나중에 포인터를 살펴볼 때 알아보도록 하겠습니다.


그리고 항상 문자열의 끝은 ‘\0’ 이여야 합니다.

‘\0’을 기준으로 문자열이 끝났다는 걸 식별하기 때문입니다.

그래서 배열의 크기는 문자의 개수보다 최소 1 더 커야합니다.

그 이상으로 커도 상관없습니다.

예를 들어 크기를 10으로 설정한 string3의 경우엔 출력 결과를 통해

남는 부분은 모두 0으로 채워지는 것을 확인할 수 있습니다.

그리고 마지막 부분에는 0과 ‘\0’이 같다는 것을 확인하는 출력문이 있습니다.


만약 문자열의 끝을 ‘\0’으로 해주지 않으면 이상현상이 발생합니다.

아래의 코드를 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    char str1[] = {"teemo1"};
    char str2[7] = {"teemo2"};
    char str3[6] = {"teemo3"};
    
    printf("str1 = %s\n", str1);
    printf("str2 = %s\n", str2);
    printf("str3 = %s\n\n", str3);
    
    return 0;
}

/*
 str1 = teemo1
 str2 = teemo2
 str3 = teemo3teemo2
*/

일단 teemo1, teemo2, teemo3 모두 문자열의 길이는 6입니다.

그럼 위에서 살펴본대로 정상적이려면

이 문자열을 저장하는 배열의 크기는 최소 7이여야 합니다.

마지막에 ‘\0’이 들어가야 하기 때문입니다.

str1처럼 배열의 크기를 설정해주지 않으면 자동으로 7로 설정이 됩니다.

그런데 str3을 보면 크기를 6으로 설정했습니다.

그랬더니 str3의 출력결과가 이상하게 나옵니다.

위의 출력 결과는 mac의 Xcode에서의 출력 결과이고

윈도우의 Visual Studio에서 출력하면 더 이상하게 나옵니다.




다차원 배열


지금까지는 1차원 배열만 살펴봤습니다.

하지만 배열에는 2차원, 3차원 등 다차원 배열이 존재합니다.

4차원부터는 써본 일이 없긴 하지만 물론 4차원도 가능은 합니다.

먼저 간단하게 2차원 배열의 선언과 출력부터 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
    int n[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };
    
    for(int i=0; i<2; i++){
        for(int j=0; j<3; j++){
            printf("n[%d][%d] = %d\t\t", i, j, n[i][j]);
        }
        putchar('\n');
    }
    
    putchar('\n');
    return 0;
}

/*
 n[0][0] = 1        n[0][1] = 2        n[0][2] = 3
 n[1][0] = 4        n[1][1] = 5        n[1][2] = 6
*/

2차원 배열의 선언부를 보면 스코프 안에 스코프를 쓰는 방식인 걸 볼 수 있습니다.


다음은 처음엔 모든 요소를 0으로 설정한 3x4 배열을 만들고

그 후, 1부터 차례대로 요소를 채워나가는 코드입니다.

#include <stdio.h>

int main(void)
{
    int arr[3][4] = {0};
    int increaser = 0;
    
    printf("Original arr\n");
    for(int i=0; i<3; i++){
        for(int j=0; j<4; j++){
            printf("%d ", arr[i][j]);
        }
        putchar('\n');
    }
    putchar('\n');
    
    for(int i=0; i<3; i++){
        for(int j=0; j<4; j++){
            arr[i][j] = ++increaser;
        }
    }
    
    printf("After arr\n");
    for(int i=0; i<3; i++){
        for(int j=0; j<4; j++){
            printf("%d ", arr[i][j]);
        }
        putchar('\n');
    }
    
    putchar('\n');
    return 0;
}

/*
 Original arr
 0 0 0 0
 0 0 0 0
 0 0 0 0
 
 After arr
 1 2 3 4
 5 6 7 8
 9 10 11 12
*/


마지막으로 간단하게 3차원 배열도 선언해보겠습니다.

// 3차원 배열

#include <stdio.h>

int main(void)
{
    int a[2][3][4] = { 0 };
    int increaser = 0;
    
    for (int i = 0; i < 2; i++) {
        printf("Plane number %d\n", i);
        
        for (int j = 0; j < 3; j++) {
            for (int k = 0; k < 4; k++) {
                a[i][j][k] = ++increaser;
                printf("%d\t", a[i][j][k]);
            }
            putchar('\n');
        }
        putchar('\n');
    }
    
    return 0;
}

/*
 Plane number 0
 1    2    3    4
 5    6    7    8
 9    10    11    12
 
 Plane number 1
 13    14    15    16
 17    18    19    20
 21    22    23    24

*/




Back to the Goal


1. 배열의 마지막 열/행을 해당 열/행의 요소의 합으로 채우기

c_array_goal1

#include <stdio.h>

int main(void)
{
    int n[3][4] = {
        {1, 2, 3},
        {4, 5, 6}
    };
    
    printf("초기 배열\n");
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d\t", n[i][j]);
        }
        putchar('\n');
    }
    putchar('\n');
    
    // 행의 합 계산하여 채워넣기
    for (int i = 0; i < 3; i++) {
        n[i][3] = n[i][0] + n[i][1] + n[i][2];
    }
    
    // 열의 합 계산하여 채워넣기
    for (int j = 0; j < 4; j++) {
        n[2][j] = n[0][j] + n[1][j];
    }
    
    printf("행/열의 합 계산 후 배열\n");
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d\t", n[i][j]);
        }
        putchar('\n');
    }
    
    putchar('\n');
    
    return 0;
}

/*
 초기 배열
 1    2    3    0
 4    5    6    0
 0    0    0    0
 
 행/열의 합 계산 후 배열
 1    2    3    6
 4    5    6    15
 5    7    9    21
*/


2. 달팽이 배열 채우기

c_array_goal2

// 달팽이 배열 채우기

#include <stdio.h>

int main(void)
{
    int n[5][5] = { 0 };
    int increaser = 0, x = -1, y = 0;
    
    while (increaser < 25) {
        
        // 우로 가는 경우
        if (x < 4 && n[y][x + 1] == 0) {
            while (x < 4 && n[y][x+1] == 0) n[y][++x] = ++increaser;
        }
        // 하로 가는 경우
        else if (y < 4 && n[y + 1][x] == 0) {
            while (y < 4 && n[y + 1][x] == 0) n[++y][x] = ++increaser;
        }
        // 좌로 가는 경우
        else if (x > 0 && n[y][x - 1] == 0) {
            while (x > 0 && n[y][x - 1] == 0) n[y][--x] = ++increaser;
        }
        // 상으로 가는 경우
        else if (y > 0 && n[y - 1][x] == 0) {
            while (y > 0 && n[y - 1][x] == 0) n[--y][x] = ++increaser;
        }
    }
    
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) printf("%d\t", n[i][j]);
        putchar('\n');
    }
    
    putchar('\n');
    return 0;
}

/*
 1    2    3    4    5
 16    17    18    19    6
 15    24    25    20    7
 14    23    22    21    8
 13    12    11    10    9
*/