학생 100명의 성적을 처리하는 프로그램을 만든다고 가정해 보겠습니다. 만약 배열이라는 개념이 없다면 우리는 score1, score2, score3처럼 변수를 100개나 따로 선언해야 하는 끔찍한 노가다를 마주해야 합니다. C 언어는 동일한 성격의 데이터들을 한 줄로 길게 묶어서 단 하나의 이름으로 관리할 수 있는 배열(Array)이라는 강력한 수단을 제공합니다. 더 나아가 우리가 매일 사용하는 글자들의 묶음인 문자열(String) 역시 C 언어 내부에서는 문자들을 한 줄로 세운 '문자 배열'로 취급됩니다. 컴퓨터가 메모리 공간에 데이터를 일렬로 배치하는 방식의 기초가 되는 배열과 문자열의 메커니즘을 확실하게 파헤쳐 보겠습니다.

📌 핵심 요약 3줄
- 연속된 메모리 공간: 배열은 같은 자료형의 데이터들을 메모리상에 빈틈없이 연속적으로 이어 붙여 저장하는 효율적인 자료구조입니다.
- 문자열과 널 문자(\0): C 언어에서 문자열은 문자 배열의 형태를 가지며, 컴퓨터가 문자열의 끝을 식별할 수 있도록 항상 맨 마지막에 '널 문자'를 포함해야 합니다.
- 라이브러리를 통한 확장: <string.h> 헤더가 제공하는 표준 함수들을 활용하면 복잡한 문자열 복사, 비교, 길이 계산을 단 한 줄로 간단하게 처리할 수 있습니다.
1. 같은 데이터의 일렬 종대, 배열 (Array)
배열은 쉽게 말해 방 번호(인덱스)가 매겨진 아파트라고 생각하면 편합니다. 단, 같은 크기의 방에 같은 종류의 자료형만 들어갈 수 있다는 규칙이 있습니다.
| 배열 용어 | 기능 및 특징 설명 | 주의해야 할 점 |
| 선언 (Declaration) | 자료형, 배열 이름, 사용할 칸의 개수를 대괄호[]안에 명시합니다. | 크기는 반드시 명확한 상수나 숫자로 지정해야 합니다. |
| 초기화 (Initialization) | 중괄호{}를 사용해 선언과 동시에 공간에 값을 채워 넣습니다. | 초기화 값을 채울 때 배열 크기를 생략하면 자동으로 크기가 잡힙니다. |
| 인덱스 (Index) | 배열의 특정 칸에 접근하는 방 번호입니다. | 방 번호는 1이 아니라 0부터 시작한다는 점을 명심해야 합니다. |
💻 1차원 배열 및 다차원 배열 선언과 접근 예제
#include <stdio.h>
int main() {
// 1. 1차원 배열 선언과 동시에 초기화 (방은 5개지만 번호는 0부터 4까지)
int numbers[5] = {10, 20, 30, 40, 50};
printf("--- 1차원 배열 출력 ---\n");
for (int i = 0; i < 5; i++) {
printf("numbers[%d] 방의 값: %d\n", i, numbers[i]);
}
// 2. 2차원 배열 (행과 열로 이루어진 바둑판 모양 구조: 2행 3열)
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
printf("\n--- 2차원 배열 특정 위치 출력 ---\n");
printf("matrix[0][1] (0행 1열): %d\n", matrix[0][1]); // 2 출력
printf("matrix[1][2] (1행 2열): %d\n", matrix[1][2]); // 6 출력
return 0;
}
2. 문자의 연속, 문자열 (String)
C 언어에는 string이라는 별도의 독립된 자료형이 기본적으로 존재하지 않습니다. 대신 문자 한 글자를 담는 char 타입을 배열로 길게 늘어뜨려 문자열을 표현합니다.
| 문자열 표현 방식 | 내부 구조 및 메모리 할당 | 특징 |
| 문자 배열 방식 | char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; | 문자 하나하나를 수동으로 넣으며 끝에 \0을 수동 입력합니다. |
| 문자열 리터럴 방식 | char str[] = "Hello"; | 큰따옴표를 쓰면 맨 뒤에 널 문자(\0)가 자동으로 포함됩니다. |
- 널 문자(\0)가 필요한 이유: 컴퓨터는 메모리에 든 데이터가 문자열인지 숫자인지 구별하지 못합니다. 일렬로 배치된 문자들을 읽다가 \0(아스키코드 0)을 만나는 순간 "아, 여기가 문자열의 끝이구나!" 하고 출력을 멈추게 됩니다. 따라서 문자가 5개인 "Hello"를 저장하려면 널 문자가 들어갈 공간까지 합쳐서 최소 6바이트의 공간이 필요합니다.
💻 문자열 입출력 예제
#include <stdio.h>
int main() {
char greeting[] = "Hello, World!";
printf("서식문자 %%s로 출력: %s\n", greeting);
char name[50];
printf("이름을 공백 없이 입력해 주세요: ");
// 문자열 배열 이름 자체가 주소를 뜻하므로 scanf에 & 기호를 붙이지 않습니다.
scanf("%s", name);
printf("%s님, 소프트웨어 공장에 오신 것을 환영합니다!\n", name);
return 0;
}
3. 문자열 요리를 위한 도구 세트, <string.h>
C 언어는 문자열을 더 편하게 가공할 수 있도록 강력한 전용 라이브러리 함수들을 기본으로 지원합니다. 이 함수들을 쓰려면 소스 코드 맨 위에 #include <string.h>를 반드시 선언해 주어야 합니다.
| 함수 이름 | 주요 기능 설명 | 간단한 비유 |
| strlen(문자열) | 순수 글자 수를 계산하여 반환합니다. (널 문자는 제외하고 측정) | 자로 글자 길이 재기 |
| strcpy(대상, 원본) | 원본 문자열을 대상 배열에 그대로 복사해서 붙여넣습니다. | 글자 그대로 복사/붙여넣기 |
| strcat(대상, 원본) | 원래 있던 대상 문자열 맨 뒤에 원본 문자열을 이어 붙입니다. | 기차 이어 붙이기 |
| strcmp(문자열1, 문자열2) | 두 문자열의 아스키코드 값을 비교하여 같으면 0을 반환합니다. | 저울질하여 글자 비교하기 |
💻 문자열 내장 함수 활용 및 뒤집기 실습 예제
#include <stdio.h>
#include <string.h> // 문자열 표준 함수들이 담긴 헤더 파일
int main() {
char str[] = "C_Language";
printf("1. 'C_Language'의 순수 글자 길이: %zu\n", strlen(str));
// 2. 응용: 문자열 양방향 정렬을 이용한 뒤집기(Reverse) 로직
char target[] = "Hello";
int len = strlen(target); // 길이는 5
for (int i = 0; i < len / 2; i++) {
char temp = target[i];
target[i] = target[len - i - 1];
target[len - i - 1] = temp;
}
printf("2. 'Hello'를 뒤집은 결과: %s\n", target); // olleH 출력
return 0;
}
💡 C 언어 배열과 문자열 사용을 위한 꿀팁 (Tip)
- 배열의 크기를 동적으로 구하려면 sizeof를 나누세요: 배열의 전체 방 개수를 코드 내에서 유연하게 계산하고 싶을 때는 sizeof(배열이름) / sizeof(배열[0]) 수식을 쓰면 됩니다. 배열의 전체 바이트 크기에서 방 딱 한 칸의 자료형 크기를 나누는 원리인데, 데이터 개수가 바뀌어도 반복문 조건을 수정할 필요가 없어서 아주 유용합니다.
- 공백이 포함된 문자열 입력은 fgets를 쓰세요: scanf("%s", 변수)는 편리하지만 스페이스바(공백)나 탭 문자를 만나는 순간 입력이 끝난 것으로 인식합니다. "Hong Gil Dong"처럼 중간에 공백이 들어간 문자열을 통째로 안전하게 입력받고 싶다면 문장 한 줄을 통째로 읽어오는 fgets() 함수를 사용하는 것을 권장합니다.
- 문자열 비교 시 == 기호는 절대 금물입니다: 숫자형 변수들과 달리 if (str1 == str2) 같은 조건식은 문자열 내용이 같은지 비교하는 것이 아니라, 두 배열이 위치한 메모리의 '주소값'이 같은지 비교하게 됩니다. 알맹이 글자들이 같은지 확인하고 싶을 때는 무조건 strcmp() 함수를 호출해야 합니다.
⚠️ 초보자가 흔히 하는 실수 (Mistakes)
- 배열의 인덱스 경계 초과 (Buffer Overflow): 크기가 5인 배열 int arr[5]를 만들었다면 쓸 수 있는 방 번호는 arr[0]부터 arr[4]까지입니다. 많은 입문자들이 무심코 arr[5] = 100; 처럼 존재하지 않는 여섯 번째 방에 값을 던지곤 합니다. 이 경우 프로그램이 쥐도 새도 모르게 다운되거나, 다른 엉뚱한 변수의 메모리 값을 오염시키는 무서운 버그를 유발합니다.
- 문자 배열 선언 시 널 문자 공간 누락: 4글자 단어인 "Cool"을 문자 배열에 직접 담으려고 char word[4] = "Cool"; 이라고 선언하는 실수를 자주 합니다. 널 문자(\0)가 들어앉을 공간이 없기 때문에 이 배열을 printf("%s")로 출력하면 컴퓨터는 끝을 찾지 못해 "Coolㅁㄴㅇㄹ!@#$" 처럼 메모리 뒤편에 밀려있던 끔찍한 쓰레기 값을 함께 출력하게 됩니다. 글자 수보다 무조건 1칸 이상 여유 있게 배열 크기를 잡으셔야 합니다.
- 문자열을 대입 연산자 =로 복사하려는 시도: 이미 선언된 문자 배열에 str = "NewString"; 처럼 대입 연산자로 문자열을 통째로 집어넣으려고 하면 컴파일 에러가 발생합니다. 배열의 이름은 상수 주소이기 때문에 값을 직접 대입할 수 없습니다. 문자열 내용을 바꾸고 싶을 때는 반드시 strcpy(str, "NewString"); 처럼 복사 함수를 통과시켜야 합니다.
🔚 맺음말
이번 시간에는 수많은 데이터 정리를 한 번에 해결해 주는 배열과, 대화형 프로그램의 기초가 되는 문자열의 핵심 메커니즘을 꼼꼼하게 짚어보았습니다. 데이터들이 메모리에 나란히 연속적으로 배치되어 숨 쉬고 있다는 물리적인 공간 개념을 머릿속에 그릴 수 있다면 이번 단원을 아주 훌륭하게 완수하신 겁니다.
오늘 내용 중 헷갈리는 부분이 있다면 언제든 소프트웨어 공장 댓글 창을 적극적으로 활용해 주세요!
'Core Programming > C for Systems Engineering' 카테고리의 다른 글
| C언어 함수 총정리: 매개변수 전달 방식(값/참조)부터 함수 포인터까지 완벽 마스터 (0) | 2024.12.13 |
|---|---|
| C언어 포인터 완벽 가이드: 주소 연산자 개념부터 배열과의 관계 총정리 (0) | 2024.12.13 |
| C언어 제어문 기초: 조건문(if, switch)과 반복문(for, while) 완벽 정리 (0) | 2024.12.13 |
| C언어 연산자 총정리: 산술, 논리, 삼항 연산자부터 비트 연산까지 (0) | 2024.12.12 |
| C언어 입출력 마스터: printf/scanf부터 파일 입출력(fopen, fclose)까지 (0) | 2024.12.12 |