[2-6] 배열

배열(array)은 메모리상에 자료형 크기 단위로 연속되게 배열된 자료 구조입니다. int형, char형, float형 등 다양한 자료형을 담을 수 있습니다.

다음과 같이 사용합니다.

자료형 변수명[크기] = {초깃값}
  • 자료형 : 할당할 메모리 크기의 단위입니다.
  • 변수명 : 배열 변수의 이름입니다.
  • 크기 : 자료형 수입니다.
  • 초깃값 : 배열 변수를 할당할 때 주어지는 값입니다. 아무것도 입력하지 않으면 0으로 자동할당됩니다. 하나 이상일 때는 쉼표로 구분합니다.

예를 들어 int형 5개를 할당하는 배열 변수 arr[ ]은 다음과 같이 할당합니다.

int arr[5] = {10, 11, 12, 13, 14};

int형 배열 변수 arr는 메모리에 다음과 같이 할당됩니다.

인덱스 0 1 2 3 4
요소값 10 11 12 13 14
자료형 int 크기 int 크기 int 크기 int 크기 int 크기

배열을 알려면 다음과 같이 두 가지를 알아야 합니다.

  • 인덱스 : 배열이 메모리에 위치한 주소를 기준으로 떨어진 거리입니다.
  • 요소 : 배열에 들어있는 값 자체입니다.

그럼 정말로 이렇게 할당되는지 확인합시다.

#include <stdio.h>

int main(void) {
	int size = 5;
	int arr[5] = {10, 11, 12, 13, 14};
	
	for(int i = 0 ; i < size ; i++)
	{
		printf("index = %d, address = 0x%x, value = %d\n", 
                    i, &arr[i], arr[i]);
	}
	return 0;
}

[출력]

index = 0, address = 0x5bf4ec30, value = 10
index = 1, address = 0x5bf4ec34, value = 11
index = 2, address = 0x5bf4ec38, value = 12
index = 3, address = 0x5bf4ec3c, value = 13
index = 4, address = 0x5bf4ec40, value = 14

배열의 주소값은 &붙여 확인할 수 있군요! 배열값은 배열 이름 뒤에 있는 대괄호에 인덱스값만 넣으면 되네요. arr[0]이라고 하면 첫 번째 값을 출력하는 거죠!

그럼 arr[5]가 어떻게 메모리에 할당되었는지 그림으로 살펴보겠습니다. 주소는 너무기니까 편의상 끝 네 자리만 살펴보겠습니다(어차피 앞 4자리는 같습니다).

인덱스 0 1 2 3 4
주소(예) 0xec30 0xec34 0xec38 0xec3C 0xec40
요소값 10 11 12 13 14

표로 살펴보니뭐가 더 잘 보이는군요!

  • 인덱스는 0부터 시작합니다.
  • 주소는 자료형의 바이트 크기 단위로 증가합니다.
  • 요소는 인덱스 0부터 순서대로 대입됩니다.

배열에 대한 정의를 다시 상기해봅니다.

메모리상에 자료형 크기 단위로 연속되게 배열된 자료 구조!

정의에 맞게 해석을 해볼까요?

arr[ ]은 메모리상에 int형 단위로 5번 연속된 자료 구조입니다.

틀림없군요!

다양한 초기화 방법

배열을 초기화하는 방법은 다음과 같이 세 가지입니다.

	int arr1[5] = {10, 11, 12, 13, 14};  // 배열 크기와 초깃값 지정
	int arr2[] = {10, 11, 12, 13, 14};   // 배열 크기를 생략한 경우
	int arr3[5];				 // 배열 크기만 지정한 경우

세 배열 변수 모두 크기는 5입니다. arr2는 배열 크기를 아예 지정하지 않았군요! 이렇게 배열 크기를 지정해주지 않으면 초기화 요소 수랑 배열 크기가 같게 자동 설정됩니다. 그래서 셋다 배열 크기가 5인 겁니다.

배열 arr1과 arr2은 모두 배열 요소로 10, 11, 12, 13, 14을 갖습니다. 그럼 arr3은 어떤 요소를 가질까요?

코드를 돌려보며 확인해보겠습니다.

#include <stdio.h>

int main(void) {
	int size = 5;
	int arr1[5] = {10, 11, 12, 13, 14};
	int arr2[] = {10, 11, 12, 13, 14};
	int arr3[5];
	
	for(int i = 0 ; i < size ; i++)
	{
		printf("arr1[%d](%d), ", i, arr1[i]);
		printf("arr2[%d](%d), ", i, arr2[i]);
		printf("arr3[%d](%d)\n", i, arr3[i]);
	}
	return 0;
}

[출력]

arr1[0](10), arr2[0](10), arr3[0](1)
arr1[1](11), arr2[1](11), arr3[1](0)
arr1[2](12), arr2[2](12), arr3[2](1101731821)
arr1[3](13), arr2[3](13), arr3[3](21992)
arr1[4](14), arr2[4](14), arr3[4](0)

[출력]

배열 arr1과 arr2는 예상대로 값이 잘 출력되었습니다. 그런데 arr3의 요소는 특정한 패턴이 없습니다. 초기화하지 않은 메모리 영역을 할당받았기 때문에 해당 메모리에 기존에 있던 값을 갖게 된 겁니다. 이런 값을 쓰레기값이라고 합니다. 예상이 불가능한 쓸 수 없는 값이기 때문입니다.

그렇다면 arr3을 선언한 이후에 늦게 나마 다음과 같이 요소를 넣어보는 건 어떤가요?

arr3 = {10, 11, 12, 13, 14};

위와 같이 하면 간편하겠네요! 그런데 초기화 이후에 위와 같이 초기화를 하면 컴파일 에러가 납니다.

아쉽게도 C 언어는 위처럼 쉬운 대입 연산을 지원하지 않습니다. 다음과 같이 각 인덱스마다 값을 직접 대입해주어야 합니다.

arr3[0] = 10;
arr3[1] = 11;
arr3[2] = 12;
arr3[3] = 13;
arr3[4] = 14;

그래서 배열 쓰이는 곳에 다음과 같은 for문이 단짝처럼 쓰입니다.

#include <stdio.h>

int main(void) {
	int size = 5;
	int arr3[5];
	
	for(int i = 0 ; i < size ; i++)
	{
		arr3[i] = 10 + i;
		printf("arr3[%d] = %d\n", i, arr3[i]);
	}
	return 0;
}

[출력]

arr3[0] = 10
arr3[1] = 11
arr3[2] = 12
arr3[3] = 13
arr3[4] = 14

원하는 결과가 잘 얻어졌군요!

연습하는 의미로 ‘char형으로 배열을 선언하고 “hello wolrd”를 초깃값으로 설정’하는 예제를 만들어보세요!