뭐라도 쓰겠지
25.03.11 / 배열 본문
배열(Array)은 같은 형태의 많은 데이터를 반복문으로 처리하기 위해 메모리에 연속적으로 저장해 놓고 쪼개서 사용하는 방법이다.
배열명은 arr[0], 첫 번째 배열 요소의 주소값을 갖게 되는데, 예를 들어 int arr[5]의 배열이 메모리 100번지부터 할당되면 int형 변수의 크기가 4바이트이기 때문에 각 요소의 주소는 100, 104, 108, 112, 116번지가 된다. 따라서 각 배열 요소는 일정한 간격(자료형의 크기)로 주소를 갖게 된다. 그리고 배열명은 첫번째 주소, arr[0]의 주소를 가지게 된다.
코드를 보며 더 자세히 알아보자.
int arr[5] = { 1, 2, 3, 4, 5 }
자료형 변수명[요소 개수] = { 초기값 };
배열을 선언하는 형태다. int형의 변수 5개를 연속으로 할당한 것이기 때문에 총 20바이트가 할당된다. 배열의 나누어진 조각을 요소(Element)라고 하는데, 각각의 요소는 int형 변수와 똑같이 사용한다. 배열 요소는 배열명에 첨자(Index)를 붙여 표현하고 첨자는 0부터 시작한다. 예를 들어 세 번째 요소인 3을 불러오려면 arr[2]를 불러오면 된다.
int arr1[5] = { 1, 2, 3, 4 }
int arr2[5] = { 1, 2, 3, 4, 5, 6 } // 오류발생
배열을 초기화 할 땐 요소 개수보다 적게 입력하면 남은 요소는 0으로 초기화 되지만, 요소 개수보다 더 많은 요소를 입력한 경우엔 오류가 발생한다. 이는 배열에게 할당된 메모리 영역을 넘어 다른 영역을 침범하게 되어서 그 영역이 어떤 용도로 사용하는지에 따라 결과가 달라지는 등 의도하지 않은 일이 일어나기 때문이다.
위의 코드는 요소 개수를 직접 선언해 배열을 선언했다. 만약 요소의 개수가 바뀐다면 배열을 처리하는 반복문을 모두 수정해야 하는데, 이 문제를 해결하기 위해 배열 요소의 개수를 계산해 반복문에 사용하는 방법을 쓸 수 있다.
배열 요소의 개수 = sizeof(배열명) / sizeof(배열 요소)
배열명은 위에서 첫번째 요소의 주소를 가진다 했다. 이는 곧 배열명이 주소값을 가지는 포인터 변수라는 말이다. 포인터 변수는 정수처럼 보이지만 자료형에 관한 정보를 가지고 있는 특별한 값이고, 따라서 연산을 자유롭게 할 수 없고 정해진 연산만 가능하다. 정수 덧셈이 대표적인데, 다음과 같은 방식으로 수행된다.
주소 + 정수 = 주소 + (정수 * 주소를 구한 변수 자료형의 크기)
예를 들어 크기가 4바이트인 int형 변수 a의 주소 100번지에 정수 1을 더한 결과는 101이 아니라 int형 4바이트를 더한 104번지가 된다. 2를 더하면 108번지.
배열에는 요소의 개수가 필요하다 했다. 무언가의 개수는 당연히 정수형이다. 그래서 배열이 필요로 하는 요소 개수에는 리터럴 상수가 들어가야한다. (컴파일 타임에서 상수로 취급되는 값을 넣어야한다) 그래서 변수를 const로 상수화해 요소 개수 자리에 넣는다 해도 인식되지 않는다. 이렇게 우리가 직접 배열의 크기를 정하는 (요소 개수에 상수를 입력하는) 배열을 정적 배열(Staic Array)라 칭한다.
이번엔 배열의 복사 방식에 대해 알아보자.
int arr1[3] = { 1, 2, 3 };
int arr2[3] = arr1;
이렇게 선언해서 배열이 복사된다면 좋겠지만, 아쉽게도 그렇지 않다. arr1은 arr1[0]의 주소를 가지고 있는 포인터 변수이고, 이걸 arr2[3]에 대입한다해서 내용이 복사되진 않는다.
그럼 배열을 복사하려면 어떻게 해야 할까?
int arr1[3] = { 1, 2, 3 };
int* pArr2 = arr1;
이 방식은 포인터를 이용해 배열을 복사하는 방법이다. pArr2는 arr1의 주소를 참조(Reference)하는 포인터 변수이고, 이런 복사 방식을 옅은 복사(Shallow Copy)라 부른다. 이 방식은 pArr2가 arr1의 주소를 참조하는 방식이기 때문에 복사를 한 뒤에 arr1의 값을 변경하면 그걸 참조하는 pArr2의 값도 바뀌게 된다.
만약 복사를 해서 별개의 배열로 사용하고 싶다면 어떤 방식을 써야 할까?
int deepArr1[3] = { 1, 2, 3 };
int deepArr2[3] = { 0 };
for (int i = 0; i < 3; ++i) {
deepArr2[i] = deepArr1[i];
}
이렇게 배열을 통째로 복사하는 것이 아닌 요소 하나하나를 복사하는 방법이 있다. 이를 깊은 복사(Deep Copy)라 부른다. 값을 똑같이 가지지만 별개의 배열인 deepArr2가 생기는 것이다.
#define ARR_LEN 3
int arr[ARR_LEN] = { 1, 2, 3 };
이런 식으로 컴파일 타임에서 상수로 인식되는 define을 사용해 선언해 요소 개수의 자리에 정수말고 다른 걸 넣을 수도 있다. 변수로 선언하고 싶다면 동적 배열을 참조하자.
'프로그래밍 > C' 카테고리의 다른 글
| 25.03.11 / 다중 차원 배열 (0) | 2025.03.11 |
|---|---|
| 25.03.11 / 동적 배열 (0) | 2025.03.11 |
| 25.03.10 / 메모리의 동적 할당 (0) | 2025.03.10 |
| 25.03.07 / 삼각수를 이용해 중첩 반복문 없이 피라미드 출력하기 (0) | 2025.03.07 |
| 25.03.07-25.03.11 / 중첩 반복문을 사용한 피라미드 (0) | 2025.03.07 |