배열의 정의
배열(Array)은 동일한 자료형의 연속된 메모리 집합이며 포인터와 인덱스 연산자로 구현됩니다.
배열의 선언
#include <stdio.h>
int main(int argc,char** argv)
{
int ArrayA[4];
int ArrayB[4] = {0, 1 ,2 ,3};
ArrayA[0] = 0;
ArrayA[1] = 1;
ArrayA[2] = 2;
ArrayA[3] = 3;
printf("ArrayA : %d %d %d %d\n",ArrayA[0],ArrayA[1],ArrayA[2],ArrayA[3]);
printf("ArrayA Address : %x %x %x %x\n",&ArrayA[0],
&ArrayA[1],&ArrayA[2],&ArrayA[3]);
printf("ArrayA First Address : %x\n",ArrayA);
printf("ArrayB : %d %d %d %d\n",ArrayB[0],ArrayB[1],ArrayB[2],ArrayB[3]);
printf("ArrayB Address : %x %x %x %x\n",&ArrayB[0],
&ArrayB[1],&ArrayB[2],&ArrayB[3]);
printf("ArrayB First Address : %x\n",ArrayB);
}
배열의 선언은 기존의 변수 선언과 비슷합니다.
기존 변수 선언문 형식에서 변수 이름 뒤에 [연속된 자료형의 수]의 형태를 덧 붙여주기만 하면 됩니다.
위의 코드에서는 int 형 4개, 즉 int ArrayA[4]; 를 사용하였습니다.
- 하나의 배열로 이루어진 배열을 1차원 배열이라고 합니다.
- 뒤에 []를 더 늘리는 방법으로 2차원, 3차원 ... 다차원의 배열을 선언할 수 있습니다.
int ArrayA[2][2];
int ArrayA[2][2][2];
- ArrayA[2][2]는 ArrayA[2]가 연속적으로 2개 모인 배열입니다.
- ArrayA[4][2]는 ArrayA[2]가 연속적으로 4개 모인 배열입니다.
의미상으로 표현하면 위와 같은 형태입니다.
배열은 여러개의 데이터를 하나의 이름으로 다루는 만큼, 처음 초기화에만 한해서 내용을 바로 대입하는
{0번 변수내용,1번 변수내용,2번 변수내용,....} 의 형태를 사용할 수 있습니다.
int ArrayB[4] = {0, 1 ,2 ,3};
위와 같은 식의 경우, ArrayB라는 이름을 가지는 int 형 4개의 배열이며, 0번째에 0, 1번째에 1, 2번째에 2, 3번째에 3이라는 수가 초기화됩니다.
다차원 배열의 초기화는 각 변수의 내용을 다시 배열로 보고 대입합니다.
int ArrayA[2][4] = { {1,2,3,4},{5,6,7,8} };
배열과 인덱스 연산자
위 예제의 ArrayA의 경우, 배열을 만들고 다시 하나씩 직접 값을 대입하고 있습니다.
배열에서 특정한 위치를 지정할때는 변수이름[번호]의 형태를 사용합니다.
여기서 []를 인덱스 연산자라고 합니다.
- 배열 선언을 하게 되면 (해당 자료형 크기) * 갯수만큼 인접한 메모리가 할당됩니다.
- 배열의 이름은 그중 가장 첫번째 주소를 가르키는 포인터로써 지정됩니다.
인덱스 연산자는 단순한 포인터 연산입니다.
- 인덱스 연산자를 사용할때 ArrayA[N]이라고 하면 N에는 어떠한 임의의 숫자가 들어갈 수 있습니다.
- 모든 메모리가 순서대로 인접해있다고 가정하면, N 번째의 배열주소는 배열의 시작주소 + (int 형의 크기 * N)가 됩니다.
ArrayA의 주소값은 정확하게 4byte 차이가 나는 형태로 이루어져 있습니다.
다차원 배열 역시 위와 같은 형태의 포인터 연산을 합니다. 따라서 배열은 매우 단순한 자료구조형이면서도 매우 강력한 자료구조형입니다.
배열로 선언할 경우 해당 범위안의 메모리는 전부 인접한 상태가 됩니다.
- 100 번째 자료를 찾고 싶다고 한다면 시작주소 + (int 형 크기 * 100)를 탐색할 수 있습니다. 이를 Random Access 라고 합니다.
- 빠른 색인을 가능하게 합니다. 다만, 메모리가 인접해 있어야 해서, 한번에 많은 크기를 할당할 수 없습니다.
메모리에도 단편화 현상이 존재합니다.
- 10MB 30MB 10MB를 할당한 뒤에 앞 뒤 10MB의 할당을 해제하면 총 20MB의 여유공간이 생긴 샘이지만, 연속된 메모리로는 10MB 이상 할당할 수가 없습니다.
- 메모리는 OS가 제공해주는 가상 메모리지만, 메모리 자체의 단편화를 피해가는 것은 어렵습니다.
Exercise
#include <stdio.h>
int main(int argc,char** argv)
{
int Array[4] = {0,1,2,3};
int* ArrayPtr = Array;
printf("%d %d %d %d\n",ArrayPtr[0],ArrayPtr[1],ArrayPtr[2],ArrayPtr[3]);
ArrayPtr = &Array[2];
printf("%d %d",ArrayPtr[0],ArrayPtr[1]);
}
Array 배열은 초기화와 동시에 0,1,2,3의 값을 가집니다.
그리고 ArrayPtr 포인터는 Array 배열의 첫 주소값(배열명을 이용)을 대입 받습니다.
이후 ArrayPtr 에 인덱스 연산자를 붙여 마치 배열처럼 사용해 보았습니다.
- 정확히는 배열처럼이 아니라, 둘 다 포인터라는 사실에 주의해 주세요.
그리고 ArrayPtr 을 Array[2]의 주소로 바꾸어 놓았습니다.
그리고 0번과 1번을 출력했습니다. 2번을 출력하려고 들면 Array 배열에 4번이 없기 때문에 ( 2+2 = 4 ) 에러가 발생할 것입니다.
#include <stdio.h>
int main(int argc,char** argv)
{
int Array[3][2];
printf("%x %x %x %x %x %x\n",&Array[0][0],&Array[0][1],
&Array[1][0],&Array[1][1],&Array[2][0],&Array[2][1]);
}
그렇다면 다차원 배열의 주소는 어떻게 될까요?
Array[0]은 Array[0][0]을 나타내는 포인터가 되며, Array[1]은 Array[1][0]을 나타내는 포인터입니다.
2차원 배열이지만 순서대로 주소값이 4씩 (int 형) 정확히 차이난다는것을 알 수 있습니다.