배열(Array)
C 언어에서 배열(Array)은 같은 타입의 여러 변수를 하나의 이름으로 관리하는 데이터 구조.
배열을 사용하면 연속된 메모리 공간에 데이터를 효율적으로 저장하고 접근할 수 있다.
배열의 각 요소는 인덱스(Index)로 접근할 수 있으며, 인덱스는 0부터 시작한다.
예를 들어, int 형의 데이터를 10개 저장할 수 있는 배열을 선언하는 방법은 다음과 같다:
int myArray[10];
이 경우 myArray는 10개의 정수(int)를 저장할 수 있는 배열이며, myArray[0]은 배열의 첫 번째 요소, myArray[9]는 배열의 마지막 요소를 나타낸다.
배열의 특징
1. 고정 크기: 배열은 선언 시에 그 크기가 결정되며, 프로그램 실행 도중에는 크기를 변경할 수 없다.
2. 동일 타입: 배열에 저장되는 모든 요소는 동일한 데이터 타입을 가져야 한다.
3. 연속된 메모리: 배열의 모든 요소는 메모리 상에서 연속적으로 위치한다. 이로 인해 데이터에 접근하는 속도가 빠르다.
4. 인덱스 접근: 배열의 각 요소(array element)에는 인덱스(index)를 통해 접근할 수 있으며, 이 인덱스를 사용하여 배열 내의 데이터를 읽거나 수정할 수 있다. [index == 첨자(subscript)]
배열은 다양한 상황에서 유용하게 사용됩니다. 예를 들어, 같은 종류의 여러 데이터를 저장하고 처리해야 할 때, 또는 데이터 집합을 반복문과 함께 처리해야 할 때 등에 효과적이다.
myArray는 배열의 시작 메모리 주소를 가진다. C compiler는 myArray를 주소 상수로 취급한다.
배열의 선언
float cost[12]; // 12개의 float형 변수를 가지는 배열 cost
char name[50]; // 50개의 char형 변수를 가지는 배열 name
char src[10], dst[10]; // 문자열 배열 src와 dst를 동시에 선언
int index, days[7]; // 일반 변수 index와 배열 days를 동시에 선언
보통 배열을 선언할 때는 배열의 크기를 #define 지시자로 만들어진 상수로 할수 있다.
#define SIZE 10
int scores[SIZE];
기존 상수로 배열의 크기를 지정하게 되면 배열의 크기를 변경하기가 쉬워진다. 즉, 프로그램의 다른 부분을 수정하지 않더라도 기존 상수의 정의만 바꿔주면 된다.
배열의 선언 (잘못된 예)
배열의 크기를 나타낼 때는 상수를 사용하여 표현한다. 변수로 배열의 크기를 사용하면 컴파일 오류가 난다.
또한 배열의 크기를 음수나 0, 실수로 하여도 모두 컴파일 오류가 난다.
int scores[size]; // 배열의 크기를 변수로 할 수 없음
int scores[-2]; // 배열의 크기가 음수이면 안 됨
int scores[6.7]; // 배열의 크기가 실수이면 안 됨
Index는 0 ~ 4까지 사용 가능
만약 프로그램이 index 5에 접근한다면?
Scores[5] = 100 치명적 오류
Compile error도 없고, 실행도 된다.
i < 7, 이런 것을 방지하기 위해 기호 상수를 많이 쓴다.
그림 2 참조
그림 2 - Memory Allocation and Out-of-Bounds Error in score.c
example 1 - 기호 상수를 이용한 배열 선언
example 2 - 평균 구하기
배열의 초기화
int myArray[5] = { 0 }; // 첫번째 요소 0, 그리고 나머지 모두를 0으로 초기화.
int myArray[] = { 10, 20, 30, 40, 50 }; // 크기가 5인 배열이 생성된다.
int myArray[5]; // 초기값이 없고, 의미없는 값들로 초기화 된다.
잘못된 예 :
int myArray[5] = { 1, 2, 3, 4, 5, 6}; // 컴파일 에러
int myArray[5];
myArray = { 1, 2, 3, 4, 5 } // 컴파일 에러
그림 3 - Array Initialization and Default Zero Padding in C
example 3 - 다양한 초기화 만들기
example 4 - 배열 요소의 개수 구하기
example 5 - 배열의 복사
example 6 - 배열의 비교
example 7 - 최소값 구하기
배열과 함수 호출:
C 언어에서 배열을 함수에 전달할 때는 배열의 전체 복사본이 전달되지 않고, 배열의 첫 번째 요소의 주소만 전달됩니다. 이 방식을 "call by reference"라고 부릅니다.
이로 인해 함수 내에서 배열을 수정하면 원본 배열에도 영향을 줍니다.
printf("main : %p\n", scores);
printf("get_average: %p\n", scores);
로 확인하자.
example 8 - Modify.c
list[2]의 값이 지역변수 int e에 복사된다.
정렬과 탐색
<선택 정렬 알고리즘은 '가장 작은 것을 선택해서 제일 앞으로 보내는' 과정을 반복하는 방식으로 배열을 정렬합니다.>
배열이 [3, 2, 9, 7, 1, 4, 8, 0, 6, 5]로 시작한다고 가정한다.
첫 번째 숫자 3은 현재 '최소값의 위치' 다.
2가 3보다 작으므로 '최소값의 위치'는 2가 다.
9, 7, 4, 8, 6, 5는 모두 2보다 크므로 무시한다.
하지만 1은 2보다 작으므로 이제 '최소값의 위치'는 1다.
0은 1보다 작으므로 '최소값의 위치'는 이제 0이 다.
이제 첫 번째 위치인 3과 '최소값의 위치'인 0을 교환한다.
배열은 이제 [0, 2, 9, 7, 1, 4, 8, 3, 6, 5]처럼 보일 것이다.
위의 과정을 배열의 끝에서 두 번째 위치까지 계속 반복한다.
example 9 - 순차 탐색
example 9 - 이진 탐색
미리 정렬되어 있어야 한다.
2^4 = 16 : 16개의 원소를 가진다면 최대 4번의 비교만으로 찾을 수 있다.
원소가 1024개 있다면 : 2^10 = 1024이므로 최대 10번만에 찾을 수 있다.
2차원 배열
2차원 배열은 행(Row)과 열(Column)의 구조를 가진 배열입니다. 일반적으로 행과 열로 구성된 표처럼 데이터를 저장할 때 유용합니다.
2차원 배열 선언
int s[3][5];
위의 선언은 2차원 배열을 정의하며, 배열의 크기는 3행과 5열로 이루어져 있습니다.
- 행(Row) : 3개의 행 (s[0], s[1], s[2])
- 열(Column) : 5개의 열 (s[0][0], s[0][1], s[0][2], s[0][3], s[0][4])
메모리 배치
그림4 에서 볼 수 있듯이, 2차원 배열 s[3][5]는 메모리에서 다음과 같이 배치됩니다:
- 행 0 : s[0][0], s[0][1], s[0][2], s[0][3], s[0][4]
- 행 1 : s[1][0], s[1][1], s[1][2], s[1][3], s[1][4]
- 행 2 : s[2][0], s[2][1], s[2][2], s[2][3], s[2][4]
각 배열 요소는 4바이트 (정수형 int) 크기의 메모리 공간을 차지하며, 연속적으로 배치됩니다.
배열 요소 접근
C 언어에서 2차원 배열의 요소에 접근하는 방법은 아래와 같습니다:
s[1][2] = 0;
이 표현은 1행 2열에 위치한 요소를 의미합니다. 그림 4에서 s[1][2]는 회색으로 강조되어 있으며, 이 위치의 값이 0으로 설정되는 것을 보여줍니다.
2차원 배열의 내부 동작
2차원 배열은 사실상 1차원 배열처럼 메모리에 배치되며, 행 우선 방식(Row-major order)으로 저장됩니다. 즉, 같은 행에 속한 요소들이 메모리에서 우선 연속적으로 저장되고 다음 행이 연속적으로 저장 됩니다.
예를 들어:
- s[0][0]의 메모리 위치 바로 다음에 s[0][1]이 오고, 그 다음에 s[0][2]가 이어집니다.
그림 4 - Visualization of a 2D Array's Memory Structure
example 10 - 2차원 배열 초기화
example 11 - 2차원 배열 초기화