C언어에서 배열은 아주 단순한 구조로 정의되어 있는데. 연속된 메모리 공간을 차지하는 같은 데이터 형태들의 집합
이다.
먼저 배열은 리스트와 다르게 정적인 데이터형이다.
따라서 소스코드 상에서 반드시 배열의 크기를 미리 정해두어야 한다는 말인데, 컴파일러는 이 정해진 크기만큼의 공간만을 확보하지 그 이상의 조치는 취하지 않는다.
배열은 다음과 같이 선언할수 있다.
데이터 타입 배열명[배열의 크기];
위의 정의에 따라서 실제로 정의를 해보면 int array[10];
과 같은 모양으로 배열을 선언할수 있다.
위와같이 정의되었을때 array
라는 배열 이름은 단 2가지 의미밖에 없는데, 첫째는 array라는 배열명은 10개의 정수가 저장된 공간의 가장 맨앞의 주소 번지를 의미한다.
두번째는 sizeof(array);
와 같이하면 배열의 크기. 즉 int형태의 data가 몇개나 저장이 될 수 있는가 하는 10 * 4 라는 값을 나타낼 수 있다.
여기서 10은 배열의 선언된 갯수를 의미하고, 4는 int의 크기인 4byte를 의미한다.
따라서 sizeof(array)를 하면 10 * 4의 값인 40이라는 값이 나오고, array의 크기를 구하고 싶다면 sizeof(array) / sizeof(int) 연산을 해주면 된다.
그 이외의 배열에 대한 조작은 모두 포인터의 조작으로 대치된다.
또 주의해야햘점은 배열의 시작하는 요소, 즉 첫번째 요소는 array[1]
이 아니라 array[0]
이다.
따라서 배열의 제일 마지막 요소는 array[10]
이 아니라 array[9]
다.
배열을 선얼할때 10이라고 작성한것은 말 그대로 배열의 크기를 정의해준것 뿐이다.
배열명 array는 배열이 확보된 연속적인 메모리 공간의 가장 첫번째 부분의 주소 번지를 의미한다.
따라서array[4]
는 포인터를 이용해*(array+4)
처럼 표현해도 완전히 동일하다.
배열에서 차수(dimension)은 배열 요소를 지정하는데 몇개의 첨자가 필요하느냐를 의미하고, 1차원 배열은 말 그대로 1개의 첨자만 있으면 된다라는 소리다.
배열은 실행후에도 값을 결정할 수 있지만, 정의와 동시에 초기값을 결정할수도 있다.
#include <stdio.h>
int main(int argc, const char * argv[]) {
int array[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for(int i = 0; i < sizeof(array) / sizeof(int); i++) {
printf("index: %d / value: %d\n", i, array[i]);
}
return 0;
}
다차원 배열은 각 배열 요소를 지정하는데 여러개의 첨자가 필요한 배열을 의미한다. 일반적으로 2차원 배열과 3차원 배열이 많이 쓰이고 그 이상은 드물다. (경험상 2차원 배열정도까지 많이 사용했던거 같다.)
#include <stdio.h>
int main(int argc, const char * argv[]) {
int array2[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
printf("%d ", array2[i][j]);
}
printf("\n");
}
return 0;
}
일반적으로 위와 같이 2차원 배열을 초기화 하는 방법을 많이 사용한다.
위의 초기화 형태를 행렬로 본다면 첫번째 첨자는 행을 지정하고 그 다음 첨자는 열을 지정하게 된다.
따라서 array2[2][1]
에 해당하는 값은 8이라는걸 알 수 있다.
그렇다면 array2[0][1]
은 바로 2라는 값을 알 수 있다.
메모리 구조도 생각보다 간단한데, array[0]
의 경우 1, 2, 3
이 연속적으로 저장된 1차원 배열의 꼴이고, array[1]
의 경우는 4, 5, 6
이 연속적으로 젖아된 1차원 배열의 형태이다.
따라서 메모리에도 연속적으로 저장되는걸 눈치챌 수 있다.
1차원 배열을 함수의 인자로 넘기는 방법은 간단하다.
포인터로 표현하는 방법과 배열로 표현하는 방법이 있지만 이 둘은 완전히 같다.
배열을 함수의 인자로 받아서 배열 요소들의 평균을 구하는 함수를 작성하면 아래와 같다.
#include <stdio.h>
#define SIZE 10
int average(int array[]) {
int sum = 0;
for(int i = 0; i < SIZE; i++) {
sum += array[i];
}
return (sum / SIZE);
}
int main(int argc, const char * argv[]) {
int array[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
printf("average: %d\n", average(array));
return 0;
}
C에서는 배열의 크기는 메모리 공간을 할당할 때만 필요하지, 실행시에는 배열의 크기에 대해서 컴파일러가 신경을 써주지는 않는다.
그래서 함수가 배열을 인자로 받을때 그 배열이 몇개의 요소를 가지는지는 전혀 알 방법이 없다.
따라서 함수 인자로 배열을 넘겨받을때 배열의 크기를 함께 인자로 넘겨받거나, 위와 같이 배열의 크기를 define해서 사용하거나 해야한다.
그리고 인자로 받는배열 array에도 []로 배열이라는 표시만 해두었지 별도로 크기를 작성하지는 않았다.
배열 자체에 배열의 끝을 나타내는 요소가 있다면 배열의크기를 넘길 필요가 없기는 하다. 예를 들어서 NULL문자가 포함된 문자열과 같은 경우다.
추가적으로 배열은 포인터와 동일한 표현이므로 average함수도 포인터의 표현으로 변경할 수 있는데 아래와 같다.
int average(int *array)
2차원 배열을 함수의 인자로 넘기는 방법은 1차원 배열을 함수의 인자로 넘기는것 대비 조금 더 주의가 필요하다.
C에서는 프로그램이 실행한 뒤에는 배열의 크기를 모른기 때문에 1차원 배열에서는 함수의 인자로 배열의 크기를 함께 넘겨주거나 혹은 이미 명시되어 있는 크기를 사용했어야 했다.
그런데 배열의 크기를 모른다고 했는데 print_array
함수의 인자에는 int array[][SIZE]
라고 적혀있어서 10이란느 크기가 들어가있다. (?)
array를 포인터라고 볼 때, array[][10]
의 표현은 array가 10개의 int형태의 공간을 가리키는 포인터이다.
즉 int array[][10]
이라는 표현은 배열의 크기를 나타낸것이 아니라, 어떤 형태의 데이터를 가리키는 포인터인지를 나타낸것이다.
즉 array가 10개의 int형태의 공간을 가리킨다는것을 알지만, 몇개인지는 모른다는 의미이다.
여튼저튼! 중요한것은 1차원 배열과 2차원 배열 모두 함수인자로 넘길때 배열의 크기도 함께 넘겨줘야 한다는걸 기억해야 한다.
만약 3차원 배열을 함수의 인자로 넘기고 싶다면!?
2차원 배열을 함수의 인자로 넘기는것의 연장선상인데, 가장 좌측에 위치한 첨자만 비워두고 나머지 첨자들은 크기를 명시하면 된다.
3 x 4 x 2 크기를 가진 3차원 배열을 함수의 인자로 넘기고 싶다고 가정할때, 함수의 헤더는 int function(int ma[][4][2])가 된다.
그 이상의 차원에서도 동일하다.