이번 문서의 목표: 1차원 배열을 선언·초기화·인덱싱하는 규칙을 정확히 알고, C의 문자열이 특수한 자료형이 아니라 널 문자로 끝나는 char 배열이라는 사실을 메모리 그림으로 이해해, 배열 경계·문자열 관련 시험형 오류를 스스로 찾아낼 수 있게 된다.
왜 배열이 필요한가
학생 30명의 점수를 저장한다고 생각해 봅시다. int score1, score2, ..., score30;처럼 변수를 30개 따로 선언하면 코드가 매우 길어지고, 반복문으로 순회하며 처리하는 것도 불가능합니다(변수 이름은 반복문의 변수처럼 바뀔 수 없기 때문입니다). 배열(array, 배열)은 같은 자료형의 값 여러 개를 하나의 이름 아래, 메모리에 연속으로 저장하는 자료 구조입니다.
쉽게 말하면: 배열은 “같은 크기의 서랍이 나란히 붙어 있는 서랍장”입니다. 서랍장 이름 하나로 전체를 가리키고, 번호(인덱스)로 개별 서랍에 접근합니다.
1. 배열 선언과 인덱싱
int scores[5];이 선언은 int 자료형 값 5개를 저장할 수 있는 연속된 메모리 공간을 확보하고, 그 시작 주소를 scores라는 이름으로 다루겠다는 뜻입니다. 각 요소는 인덱스(index, 색인)로 접근하며, C 배열의 인덱스는 항상 0부터 시작합니다.
scores[0] = 90;
scores[1] = 85;
scores[2] = 78;
printf("%d\n", scores[1]);85메모리 배치 그림
int가 4바이트라고 가정하면, scores[5]는 메모리에 다음과 같이 연속으로 배치됩니다.
| 인덱스 | scores[0] | scores[1] | scores[2] | scores[3] | scores[4] |
|---|---|---|---|---|---|
| 상대 주소(예시) | 1000 | 1004 | 1008 | 1012 | 1016 |
| 값 | 90 | 85 | 78 | (미초기화) | (미초기화) |
해석: scores[1]의 주소가 scores[0]보다 정확히 4만큼 큰 이유는, int 한 칸의 크기(4바이트)만큼씩 떨어져 빈틈없이 연속으로 배치되기 때문입니다. 이 “연속 배치”라는 성질이 다음 편(12편)에서 배울 포인터와 배열의 관계를 이해하는 핵심 열쇠입니다.
배열 인덱스는 몇 번까지 유효한가
크기가 5인 배열 scores[5]의 유효한 인덱스는 scores[0]부터 scores[4]까지, 총 5개입니다. scores[5]는 존재하지 않는 칸입니다.
int scores[5] = {90, 85, 78, 92, 88};
printf("%d\n", scores[5]);이 코드는 컴파일은 되지만, scores[5]는 배열이 실제로 확보한 메모리 범위를 한 칸 벗어난 위치입니다. C는 배열 경계를 자동으로 검사해 주지 않으므로(다른 언어와 달리 인덱스 범위 검사가 없습니다), 이런 접근은 정의되지 않은 동작(undefined behavior)이 됩니다. 실행해 보면 우연히 어떤 값이 출력될 수도, 프로그램이 비정상 종료될 수도 있습니다.
자주 틀리는 점: “크기가 5인 배열이니 인덱스도 1부터 5까지”라고 착각하면 시험에서 배열 경계를 벗어난 접근(off-by-one, 오프바이원 오류)을 놓칩니다. 크기가 n인 배열의 유효 인덱스는 항상 0부터 n-1까지입니다.
2. 배열 초기화 규칙
int a[5] = {1, 2, 3, 4, 5}; /* 전체 초기화 */
int b[5] = {1, 2}; /* 부분 초기화: 나머지는 자동으로 0 */
int c[] = {1, 2, 3}; /* 크기 생략: 초기값 개수(3)로 자동 결정 */
int d[5] = {0}; /* 전체를 0으로 초기화하는 관용적 표현 */| 선언 | a 배열 | b 배열 | c 배열 | d 배열 |
|---|---|---|---|---|
| 결과 값 | 1, 2, 3, 4, 5 | 1, 2, 0, 0, 0 | 1, 2, 3(크기 3) | 0, 0, 0, 0, 0 |
자주 틀리는 점: int b[5] = {1, 2};처럼 초기값을 배열 크기보다 적게 주면, 나머지 요소는 쓰레기 값이 아니라 자동으로 0으로 채워집니다. 단, 이는 초기화식을 명시했을 때만 적용되는 규칙입니다. int e[5];처럼 초기화식 자체가 없으면 각 요소는 쓰레기 값(이전에 그 메모리에 남아 있던 임의의 값)을 가지므로, 이 둘을 혼동하면 안 됩니다.
3. 문자열은 사실 char 배열이다
C에는 다른 언어와 달리 “문자열”이라는 전용 자료형이 없습니다. 대신 문자(char) 배열의 끝에 특수한 문자 하나를 붙여 문자열을 표현하는 관례(convention)를 사용합니다. 이 특수한 문자를 널 문자(null character, 널 문자)라고 부르며, 코드에서는 '\0'(값이 0인 문자)으로 씁니다.
char name[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
printf("%s\n", name);Hello| 인덱스 | name[0] | name[1] | name[2] | name[3] | name[4] | name[5] |
|---|---|---|---|---|---|---|
| 값 | H | e | l | l | o | \0 |
해석: printf의 %s 서식은 지정된 주소부터 문자를 하나씩 읽어 화면에 출력하다가, '\0'을 만나는 순간 즉시 멈춥니다. name 배열 자체는 6칸이지만, 화면에는 \0 앞의 5글자(Hello)만 출력됩니다. '\0'은 화면에 보이지 않는 “여기서 문자열이 끝난다”는 표시일 뿐입니다.
쉽게 말하면: C의 문자열은 “글자들이 든 배열 + 끝났다는 표시(
\0)“입니다.\0이 없으면 어디까지가 문자열인지 아무도 알 수 없습니다.
문자열 리터럴로 초기화하기
같은 문자열을 아래처럼 더 간단히 쓸 수 있습니다.
char name[6] = "Hello";"Hello"처럼 큰따옴표로 감싼 것을 문자열 리터럴(string literal, 문자열 리터럴)이라고 합니다. 이 표기는 'H', 'e', 'l', 'l', 'o', '\0'을 순서대로 넣는 것과 완전히 같은 결과를 만듭니다. "Hello"는 글자 수가 5개지만, 컴파일러가 자동으로 끝에 '\0'을 추가하기 때문에 최소 6칸이 필요합니다.
자주 틀리는 점: char name[5] = "Hello";처럼 문자열의 글자 수와 배열 크기를 정확히 맞추면 '\0'이 들어갈 자리가 없습니다. 5글자 문자열을 담으려면 반드시 6칸 이상(글자 수 + 1)의 배열이 필요합니다. 이 “+1”을 빠뜨리는 실수가 시험과 실무 모두에서 가장 흔한 문자열 버그입니다.
문자열의 길이를 재는 방법 — strlen
문자열의 실제 글자 수(널 문자는 제외)를 구할 때는 string.h 헤더의 strlen 함수를 씁니다. 문자열 라이브러리 함수 전반은 19편에서 깊이 다루며, 여기서는 널 종료 개념과 바로 연결되는 기본 동작만 확인합니다.
#include <stdio.h>
#include <string.h>
int main(void) {
char name[6] = "Hello";
printf("%d\n", (int)strlen(name));
printf("%d\n", (int)sizeof(name));
return 0;
}5
6해석: strlen(name)은 name[0]부터 '\0'을 만나기 직전까지 글자 수를 세므로 5를 반환합니다. 반면 sizeof(name)은 배열이 확보한 전체 메모리 크기(바이트 수)를 반환하므로, '\0'까지 포함한 6이 나옵니다. 자주 틀리는 점: strlen과 sizeof를 같은 개념으로 착각하는 경우가 많은데, strlen은 “글자 수를 세는 함수(실행 중 계산)“이고 sizeof는 “배열이 차지하는 바이트 크기를 알려주는 연산자(컴파일 시점에 결정)“로 근본적으로 다릅니다.
배열 이름을 통째로 대입할 수 없다
char a[10] = "Hi";
char b[10];
b = a; /* 오류! */배열 이름끼리는 =로 통째로 대입할 수 없습니다(배열 이름이 왜 이런 제약을 갖는지는 12편에서 포인터와 함께 설명합니다). 배열 전체 내용을 복사하려면 string.h의 strcpy(b, a);처럼 함수를 써야 하며, 이 역시 19편에서 자세히 다룹니다.
핵심 정리
- 배열은 같은 자료형의 값을 메모리에 연속으로 저장하며, 인덱스는 항상 0부터 시작한다.
- 크기가 n인 배열의 유효 인덱스는 0부터 n-1까지다. n을 인덱스로 쓰면 경계를 벗어난 접근이다.
- 초기화식을 일부만 주면 나머지는 0으로 채워지지만, 초기화식 자체가 없으면 쓰레기 값이 남는다.
- C의 문자열은 전용 자료형이 아니라, 끝에 널 문자
\0이 붙은 char 배열이다. - 문자열 리터럴
"문자열"은 끝에\0이 자동으로 붙으므로, 배열 크기는 글자 수보다 최소 1 이상 커야 한다. - strlen은
\0이전까지의 글자 수, sizeof는 배열 전체가 차지하는 바이트 크기를 반환한다.
마무리 복습
char a[10] = "Hi";
char b[10];
b = a;참고 자료
- 국가평생교육진흥원 독학학위제: https://bdes.nile.or.kr