728x90
배열의 필요성
배열은 왜 필요한가?
int a0;
int a1;
int a2;
int a3;
int a4;
연속되는 데이터들을 만약 각각의 변수에 담게 된다면 데이터를 조작하기가 어려워진다.
배열
- 배열(Array): 동일한 타입의 데이터를 여러 개 저장 가능한 자료구조
- 원소(요소, element): 배열 안에 들어있는 각각의 데이터
- 배열의 크기: 원소의 개수
- 원소는 연속된 메모리에 저장되며, 정수형의 번호(index)를 사용하여 접근(access)
- 인덱스(첨자, index, subscript): 배열 원소의 번호
- 인덱스는 0부터 시작
- 배열을 이용하면 여러 개의 값을 하나의 이름으로 다룰 수 있음
- 배열 선언
- int arr[10]
int - 원소의 자료형
arr - 배열의 이름
10 - 배열의 크기
배열 선언 예시
int score[10]; // 10개의 원소를 갖는 int형 배열 score
char src[10], dst[10]; // 2개의 문자형 배열 동시 선언(문자형 배열은 문자열에 쓰임)
int index, days[7] // 일반 변수 index와 7개의 원소를 갖는 int 배열 days
배열의 원소 접근
score[5] = 80; // score의 5번 원소는 80
score[1] = score[0]; // score 1번 원소는 score 0번 원소
score[i] = 100; // score i번 원소는 100
score[i + 1] = 101; // score i + 1번 원소는 101
score[index[i]] = 100; // score의 인덱스는 index의 i번 원소이며 score의 원소는 100
반복문
int score[5];
score[0] = 0;
score[1] = 0;
score[2] = 0;
score[3] = 0;
score[4] = 0;
↓
#define SIZE 5
...
int score[SIZE];
...
for (int i = 0; i < SIZE; i++)
score[i] = 0;
반복문을 통해 배열 사용 시 번거로움을 줄일 수 있음.
배열 입력
scanf("%d", &score[0]);
// 연산자 우선순위에 따라 &보다 [ ]가 더 높다.
// & = 주소 연산자, [ ] = 배열 인덱스
잘못된 인덱스
int score[5];
score[5] = 1; // 배열은 0부터 시작함.
// 0, 1, 2, 3, 4 → 인덱스 5를 참조하려하면 오류 발생
배열의 초기화
int score[5] = {1, 2, 3, 4, 5}; // 배열 "초기화" 시에만 중괄호({ }) 사용
int score[5] = {1, 2, 3} // 부분 초기화: 일부 원소만 초기화 → 나머지는 모두 0 / NULL
int score[] = {1, 2, 3, 4, 5};
// 배열의 크기를 비워두면 초기화하는 원소의 개수만큼 컴파일러가 배열의 크기를 잡음
초기화되지 않은 배열
int score[5];
// score[0 ~ 4]는 모두 알 수 없는 미정된 값(undefined value)를 가짐
배열의 크기 계산
int score[ ] = {1, 2, 3, 4, 5, 6, 7};
...
size = sizeof score / sizeof score[0];
// 배열을 sizeof 할 시 배열의 크기 * 원소의 자료형의 크기
// 그렇기 때문에 원소의 자료형의 크기만큼 나눠줌
배열의 복사
int score1[5] = {1, 2, 3, 4, 5};
int score2[5];
score2 = score1; // 컴파일 에러, 배열의 이름은 상수 취급
↓
int score1[SIZE] = {1, 2, 3, 4, 5};
int score2[SIZE];
...
for (int i = 0; i < SIZE; i++)
score2[i] = score1[i];
배열의 비교
score1[SIZE] = {1, 2, 3, 4, 5};
score2[SIZE] = {1, 2, 3, 4, 5};
if (score1 == score2)
printf("same");
else
printf("diff");
// 배열의 이름끼리 비교할 수 없음, 틀린 표현
for (int i = 0; i < SIZE; i++)
if (score1[i] != score2[i]) {
printf("diff");
return 0;
} // 배열의 원소를 하나하나 비교하여야 함
배열과 함수
int f(int a[], int n);
...
f(arr, n);
// 함수 호출 시 배열의 첫 원소의 주소가 복사되어 전달 됨(배열의 원본이 간다는 것과 비슷한 의미), 포인터에서 다룸
int f(int a[], int n) { // 배열의 원본
...
}
함수의 매개변수로서 const 배열
void f(const int a[]); // 배열의 원소를 바꾸지 않음
배열과 정렬
배열과 정렬은 매우 밀접한 관계를 갖고 있다. 유관한 동일 자료형의 집합이라는 점에서 매우 유사한데, 간단한 정렬 알고리즘인 선택 정렬에 관해 다룬다.
// 선택 정렬(Selection Sort)
#include <stdio.h>
#define SIZE 5
void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }
int main() {
int arr[SIZE] = {5, 3, 2, 4, 1};
for (int i = 0; i < SIZE - 1; i++) {
int least = i;
for (int j = i + 1; j < SIZE; i++)
if (arr[least] > arr[j])
least = j;
swap(arr[least, arr[i]);
}
for (int i = 0; i < SIZE; i++)
printf("%d ", arr[i]);
return 0;
}
정렬이 안 된 구간을 순회에서 최소값 / 최대값을 선택하여 구간의 첫 번째와 교환한다.
다차원 배열
2차원 이상의 배열을 다차원이라고 부른다. 이는 설명을 듣는 것보다 그림으로 보는 것이 훨씬 편리하다.
코드로 다차원 배열을 선언하는 방식 또한 매우 단순하다.
int _1d [5];
int _2d [5][5];
int _3d [5][5][5];
다차원 배열은 다루기가 까다로워 실제로 자주 쓰이진 않지만, 그나마 자주 쓰이는 것 중 하나인 2차원 배열에 관해 설명하자면, 첫 번째 [5]가 행을 뜻하며, 그 다음 [5]가 열을 뜻한다.
int arr[3][5] = {
{0, 1, 2, 3, 4},
{5, 6, 7, 8, 9},
{10, 11, 12, 13, 14}
}; // OK - 3 * 5 크기의 배열
int arr[][5] = {
{0, 1, 2, 3, 4},
{5, 6, 7, 8, 9},
{10, 11, 12, 13, 14}
}; // OK - 3 * 5 크기의 배열, 첫 번째 차원의 크기만 생략 가능
int arr[][10] = {
{0, 1, 2, 3, 4},
{5, 6, 7, 8, 9},
{10, 11, 12, 13, 14}
}; // OK - 3 * 10 크기의 배열, 생략된 원소는 부분 초기화로 모두 0이 됨
첫 번째 차원의 크기만 생략이 가능한 까닭은 두 번째 차원부터는 배열의 형태를 컴파일러에게 넘겨주어 배열의 올바른 위치에 엑세스할 수 있도록 해주어야 하기 때문이다. 배열 전체의 크기는 컴파일러가 몰라도 되지만 각 차원의 배열 크기를 알아야 올바른 위치를 찾을 수 있다.
초기화
int arr[2][2] = {
{1, 2},
{3, 4}
};
int arr[][2] = {1, 2, 3, 4};
두 방법 모두 가능하다.
인덱스 접근
for (int i = 0; i < SIZE; i++)
for (int j = 0; j < SIZE; j++)
arr[i][j] = 1; // i = 행, j = 열
형식 인자
함수에서의 형식인자는 첫 번째 차원의 크기만 생략 가능하다.
int f(int arr[][SIZE][SIZE]); // 첫 번째 크기만 생략 가능
728x90
'Programming Language > C Language' 카테고리의 다른 글
포인터 (1) | 2024.06.08 |
---|---|
변수의 범위와 생존 시간 (1) | 2024.06.07 |
변수와 함수(심화) (1) | 2024.06.07 |
함수 (0) | 2024.06.06 |
반복문 (1) | 2024.06.06 |