Home C언어 (8) 배열
Post
Cancel

C언어 (8) 배열

C언어 (8) 배열

학교의 모든 학생의 성적을 변수에 저장하고 싶은데 각각 변수를 생성하면 변수의 개수가 너무 많아진다.
이 때 같은 자료형을 가진 변수를 묶어 배열이라는 자료구조로 처리할 수 있다.

배열

image

변수란 우리가 프로그램을 작성할 때 숫자나 문자 같은 데이터를 저장하기 위한 공간이다.
변수는 저장되는 주소가 있고, 공간이 있다.
배열이란 같은 자료형을 가진 연속된 메모리 공간으로 이루어진 자료구조이다.
같은 자료형의 변수들이 여러 개 필요할 때 사용하며 많은 양의 데이터를 처리하기 유용하다.

배열의 선언

1
2
3
4
5
#include <stdio.h>
int main() {
    int ar[20];
    return 0;
}

변수의 선언과 비슷하다. 자료형, 배열의 이름이 있고 배열의 길이가 추가된다.
char, int, double 등 원하는 자료형으로 배열을 만들 수 있다.

이렇게 배열을 선언한 뒤 각각의 요소에 접근을 하려면 배열의 이름[원하는 인덱스]로 접근한다.
위에서 int ar[20]이라는 배열을 선언했으니 ar[0]=3, ar[1]=5, 이런 식으로 접근한다.
주의할 점은 배열의 시작 인덱스는 0번부터 시작하기에 n번째 요소에 접근하고 싶으면 ar[n-1]로 접근한다.
이렇게 ar[0]부터 ar[19]까지 20개의 배열 요소가 만들어졌다.
image

코드처럼 ar[2], ar[3]에는 정상적인 접근이 가능하지만 ar[20]은 범위를 초과하기에 오류가 발생한다.

배열의 초기화

이제 배열을 선언해줬으니 초기화를 해주자.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main() {
    int ar[5];
    ar[0] = 10;
    ar[2] = 15;
    ar[3] = 25;

    for (int i = 0; i < 5; i++) {
        printf("%d번째 요소 : %d\n", i, ar[i]);
    }
    return 0;
}

위에서 봤듯 변수에 값을 넣어주는 것처럼 배열 하나하나에 접근해 값을 넣을 수도 있다.
현재는 ar[0], ar[2], ar[3]에만 값을 넣어줬기에 ar[1], ar[4]에는 무슨 값이 있을까?
변수를 초기화하지 않고 선언만 했을 때와 똑같이 쓰레기 값이 들어있을 것이다.

배열은 중괄호를 이용해 쉽게 데이터를 초기화 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main() {
    int ar[5] = { 1,2,3,4,5 };
    int arr[] = { 1,2,3,4,5 }; //같은 뜻

    ar = { 4,5,6,7,8 }; // 오류 발생
    for (int i = 0; i < 5; i++) {
        printf("%d번째 요소 : %d\n", i, ar[i]);
    }
    for (int i = 0; i < 5; i++) {
        printf("%d번째 요소 : %d\n", i, arr[i]);
    }
    return 0;
}

int ar[5] = { 1,2,3,4,5 };은 각각의 원소 0,1,2,3,4번째에 1,2,3,4,5를 넣어준다.
또한 배열의 크기를 정해주지 않아도 중괄호를 통해 초기화해주면 중괄호 안의 원소 개수만큼 알아서 배열의 크기를 정해준다.
하지만 배열의 선언과 동시에 초기화해주는 것이 아니라 ar[] = { 4,5,6,7,8 }; 같이 따로하면 오류가 발생한다.

1
int ar[];

또한 배열의 크기를 정해주지 않고 선언만 하는 것은 불가능하다.
크기를 알아야 컴파일러가 메모리를 할당해주는데, 정보가 없으면 할당해줄 수 없기 때문이다.

1
2
3
4
5
6
7
8
#include <stdio.h>
int main() {
    int ar[5] = { 1,2,3 };
    for (int i = 0; i < 5; i++) {
        printf("%d번째 요소 : %d\n", i, ar[i]);
    }
    return 0;
}

그러면 배열을 전부 채워주지 않고 앞의 3개만 채우면 어떻게 출력이 될까?
다행히 ar[0] = 1, ar[1] = 2, ar[2] = 3을 해주고 나머지 ar[3], ar[4]는 0을 알아서 넣어준다.

1
2
3
4
5
6
7
8
#include <stdio.h>
int main() {
    int ar[5] = { };
    for (int i = 0; i < 5; i++) {
        printf("%d번째 요소 : %d\n", i, ar[i]);
    }
    return 0;
}
1
2
3
4
5
6
7
8
#include <stdio.h>
int main() {
    int ar[5];
    for (int i = 0; i < 5; i++) {
        printf("%d번째 요소 : %d\n", i, ar[i]);
    }
    return 0;
}

위의 코드는 초기화를 시도해 ar의 요소들을 0으로 초기화하지만
밑의 코드는 초기화 과정이 없기에 쓰레기 값이 출력된다.
2개는 크게 다르니 주의하자.

배열의 주소

배열의 정의 중 연속된 메모리 공간으로 이루어졌다는 것은 무슨 의미일까?
image

이렇게 char형 배열 char c[5]와 int형 배열 int a[3]이 있다고 하자.
연속된 메모리 공간으로 이루어져 있다는 것은 배열의 각 원소를 위한 공간이 연속된 주소에 할당된다는 것이다.
char c[5]배열의 시작 주소가 100이라고 하면, char형 변수의 크기는 1byte이므로 c[0]의 주소는 100, c[1]의 주소는 101..이다.

int a[3]의 배열의 시작 주소는 200이라고 하자. int형 변수의 크기는 4byte이다.
그러므로 200~203까지는 a[0]을 위해 할당, 204~207은 a[1]을 위해 할당된다.

  1. 배열의 0번째 인덱스의 주소는 배열의 시작 주소와 같다는 것
  2. 연속된 배열 요소의 주소값의 차이는 자료형의 크기와 같다는 것을 기억해두자.

다차원 배열

그렇다면 배열의 배열을 만들 수 있을까?
image

int ar[100] 같은 배열을 1차원 배열이라고 생각하면 2차원, 3차원..n차원 배열을 만들 수 있다.

1
2
3
4
5
6
7
8
int ar_2[5][5] = {
    {0, 1, 2, 3, 4},
    {0, 1, 2, 3, 4},
    {0, 1, 2, 3, 4},
    {0, 1, 2, 3, 4},
    {0, 1, 2, 3, 4},
};
int ar_3[5][5][5];

선언과 초기화 방법도 1차원 배열과 크게 다르지 않다.
참조 방법도 int ar_2[0][0] 부터 int ar_2[4][4]까지 접근할 수 있다.

1
2
3
4
5
6
7
int ar_2[5][5] = {
    {0, 1, 2, 3, 4},
    {0, 1, 2},
    {0, 1, 2, 3, 4},
    {0},
    {0, 1, 2, 3, 4},
};

이렇게도 2차원 배열을 초기화 할 수 있다.
1차원 배열의 초기화와 똑같이 값이 없는 부분은 알아서 0으로 채워준다.

다차원 배열의 주소

image

그림으로 다차원 배열을 이렇게 표현할 수 있다.
하지만 이는 이해하기 쉽게 만든 그림일 뿐 실제로 변수와 값이 컴퓨터에 이렇게 저장될 수는 없다.
학원에서 학생이 4차원이나 5차원 배열은 그릴 수 없는데 어떻게 값이 들어가냐고 물어본 적이 있어서..
실제로 값이 저장되는 컴퓨터의 메모리 공간은 입체적 공간이 아닌 선형 공간이다.

image

왼쪽은 1차원 배열이 저장되는 것이고, 오른쪽이 2차원 배열이다.
2차원 배열은 1차원 배열의 배열이니 1차원 배열이 여러개가 모인 것이라고 이해하면 쉽다.
arr2[0], arr2[1]은 1차원 배열이고 arr[0][0], arr[0][1]이 배열의 요소이다.
그렇기 때문에 arr2[2][3]은 arr2[0][0]부터 순서대로 arr[1][2]까지 메모리에 저장된다.

arr2[5][5]라는 배열이 있을 때 arr2[0][0]의 주소값과 arr2[2][4]의 주소값의 차이는 몇일까?

1
2
3
4
5
arr2[0][0], arr2[0][1], arr2[0][2], arr2[0][3], arr2[0][4],
arr2[1][0], arr2[1][1], arr2[1][2], arr2[1][3], arr2[1][4],
arr2[2][0], arr2[2][1], arr2[2][2], arr2[2][3], arr2[2][4],
arr2[3][0], arr2[3][1], arr2[3][2], arr2[3][3], arr2[3][4],
arr2[4][0], arr2[4][1], arr2[4][2], arr2[4][3], arr2[4][4]

배열을 보면 arr2[0][0]와 arr2[2][4]사이에는 14개의 int형 변수가 들어있다.
그렇기에 14x4 = 56의 주소값 차이가 있다.
다른 방법으로 생각하면 arr2[0]와 arr2[1]의 주소값 차이가 int(4byte) x 5 = 20이다.
arr2[1]과 arr2[2]의 차이도 20이고, arr2[2][0]과 arr2[2][4]의 차이는 4 x 4 = 16이다.
그러므로 20 + 20 + 16 = 56의 차이를 가지게 된다.

다차원 배열

3차원 배열보다 더 큰 배열도 물론 만들 수 있다.
하지만 arr5[3][3][3][3][3] 과 같은 5차원 배열이 있다고 하면 각각의 인덱스를 쓰는 것 부터 헷갈릴 것이다.
그렇기에 3차원 이상의 다차원 배열은 잘 사용하지 않는다.

배열의 응용

백준 1차원 배열
백준 2차원 배열

해당 사이트의 문제를 풀어보면 어떻게 배열을 쓸 수 있는지 볼 수 있다.


배열이라는 새로운 자료구조를 알아보았다.
이제 C언어에서 가장 어려운 부분이라고 할 수 있는 포인터를 알아보자.

This post is written by PRO.