[C 시리즈 7편] 배열과 문자열로 값 묶기

English version

6편에서 함수로 코드를 나누었다면, 이번에는 여러 값을 한꺼번에 담는 방법을 배웁니다. 배열은 같은 자료형 값을 연속된 메모리 칸에 나열하고, 문자열은 문자 배열 끝에 \0이라는 종료 표시를 붙인 형태입니다. 배열을 이해하면 나중에 포인터와 메모리 주소를 볼 때도 훨씬 수월해집니다.

이번 글에서 새로 나오는 용어

  1. 배열 (Array): 같은 자료형 값을 연속된 메모리에 순서대로 저장한 묶음
  2. 인덱스 (Index): 배열 안에서 몇 번째 값인지 나타내는 번호 (0부터 시작)
  3. 문자열 (String): 문자 배열 끝에 \0을 붙여 문자열의 끝을 알리는 데이터
  4. 널 문자 (Null Terminator): 문자열 종료를 뜻하는 \0
  5. 버퍼 (Buffer): 일정 크기의 배열을 만들어 데이터를 임시로 저장하는 공간

핵심 개념

학습 메모

  • 소요 시간: 60분 내외
  • 준비물: 함수 예제와 반복문을 자유롭게 다룰 수 있는 상태
  • 학습 목표: 배열 선언·초기화와 문자열 처리 패턴을 익히기

배열은 자료형 이름[길이] 형태로 선언합니다. 인덱스는 0에서 시작하며, scores[0]은 첫 번째 값을 의미합니다. 문자열은 사실상 char 배열이지만, 끝에 \0이 있어야만 printf 같은 함수가 어디까지 읽어야 하는지 알 수 있습니다.

코드로 따라하기

정수 배열 선언과 출력

#include <stdio.h>

int main(void) {
    int scores[5] = {95, 82, 74, 63, 88};
    int i;

    for (i = 0; i < 5; i++) {
        printf("scores[%d] = %d\n", i, scores[i]);
    }

    return 0;
}

배열 길이가 5이므로 인덱스는 0~4만 유효합니다. 예를 들어 scores[0]은 첫 번째 점수, scores[1]은 두 번째 점수입니다. 범위를 벗어나면 정의되지 않은 동작이 생기므로 항상 조건식을 배열 길이와 맞춰야 합니다.

배열 길이와 sizeof

#include <stdio.h>

int main(void) {
    int scores[5] = {95, 82, 74, 63, 88};
    size_t length = sizeof(scores) / sizeof(scores[0]);

    printf("배열 길이: %zu\n", length);
    return 0;
}

정적 배열은 sizeof(전체) / sizeof(한 칸) 공식을 사용하면 길이를 계산할 수 있습니다. 여기서 size_t는 길이와 크기를 표현할 때 자주 쓰는 부호 없는 정수형이라고 생각하면 됩니다. 함수 안에서 배열을 매개변수로 받으면 포인터처럼 동작해 이 공식이 통하지 않는다는 점도 함께 기억해 둡니다.

문자 배열과 문자열 리터럴

#include <stdio.h>

int main(void) {
    char name1[6] = {'A', 'l', 'i', 'c', 'e', '\0'};
    char name2[] = "Alice"; // 자동으로 \0 추가

    printf("name1 = %s\n", name1);
    printf("name2 = %s\n", name2);
    return 0;
}

문자열 리터럴을 사용하면 \0이 자동으로 붙습니다. 하지만 배열 길이를 직접 적을 때는 종료 문자를 포함할 만큼 충분히 크게 잡아야 합니다. 예를 들어 "Alice"를 저장하려면 최소 6칸이 필요합니다.

문자열 입력 받을 때 주의점

#include <stdio.h>

int main(void) {
    char buffer[20];

    printf("이름을 입력하세요: ");
    scanf("%19s", buffer);

    printf("안녕하세요, %s!\n", buffer);
    return 0;
}

scanf를 사용할 때는 %19s처럼 최대 길이를 지정해 버퍼 오버플로우를 막아야 합니다. 다만 %s 입력은 공백을 만나면 거기서 끊깁니다. 이름처럼 한 단어 입력에는 괜찮지만, 공백이 들어간 문장을 읽고 싶다면 나중에 배울 fgets가 더 적합합니다.

함수와 배열을 함께 사용하기

#include <stdio.h>

size_t count_warning(const int scores[], size_t length) {
    size_t count = 0;
    size_t i;

    for (i = 0; i < length; i++) {
        if (scores[i] < 60) {
            count++;
        }
    }

    return count;
}

int main(void) {
    int scores[] = {95, 82, 40, 77, 58};
    size_t length = sizeof(scores) / sizeof(scores[0]);

    size_t warning = count_warning(scores, length);
    printf("경고 인원: %zu\n", warning);
    return 0;
}

함수 매개변수로 배열을 전달할 때는 int scores[]int *scores처럼 포인터 형태가 됩니다. 그래서 함수 안에서는 배열 전체 길이 정보가 자동으로 따라오지 않습니다. 배열 길이는 따로 넘겨 주어야 범위를 안전하게 판단할 수 있습니다.

실전 예시: 문자열 붙이기

#include <stdio.h>
#include <string.h>

void make_label(char *buffer, size_t size, const char *name) {
    snprintf(buffer, size, "Student: %s", name);
}

int main(void) {
    char label[32];
    make_label(label, sizeof(label), "Minji");
    printf("%s\n", label);
    return 0;
}

snprintf는 지정한 길이만큼만 작성하기 때문에 문자열이 버퍼 크기를 넘지 않도록 도와줍니다. 아직 표준 라이브러리를 깊게 파지 않아도, "문자열도 결국 배열이므로 길이를 항상 확인해야 한다"는 감각만 잡아두면 충분합니다.

왜 중요한가

  • 배열은 데이터를 반복 처리하는 기본 단위이므로, 조건문·반복문과 함께 쓰면 프로그램이 한층 실용적으로 바뀝니다.
  • 문자열은 사용자 입력과 출력의 핵심 형태입니다. \0 규칙을 이해해야 버그를 줄일 수 있습니다.
  • 배열 길이와 버퍼 크기를 스스로 계산하는 습관이 있어야 포인터와 동적 메모리로 넘어갈 때도 안전합니다.

CodeSandbox로 이어서 실습하기

아래 샌드박스는 CodeSandbox의 Universal starter입니다. C는 터미널에서 직접 컴파일하고 다시 실행하는 흐름이 중요하니, 이번 글의 코드를 파일로 만들고 빌드-실행 사이클을 다시 따라가 보세요.

Live Practice

C Practice Sandbox

CodeSandbox

Run the starter project in CodeSandbox, compare it with the lesson code, and keep experimenting.

Universal starterCterminal
  1. starter를 fork한 뒤 hello.c 같은 실습 파일을 만든다
  2. 본문 코드를 붙여 넣고 clang 또는 gcc가 있으면 직접 컴파일한다
  3. 코드를 고친 뒤 다시 빌드하고 실행 결과를 비교한다

C는 브라우저 미리보기보다 터미널 빌드 흐름이 핵심입니다. Universal starter의 컴파일러 구성이 환경에 따라 다를 수 있으니, 먼저 clang이나 gcc 사용 가능 여부부터 확인하세요.

실습

  • 따라 하기: 정수 배열을 선언해 for 문으로 합계와 평균을 구해 봅니다.
  • 확장하기: 사용자에게 최대 3개의 이름을 입력받아 문자열 배열에 저장하고, 다시 한 번씩 출력합니다.
  • 디버깅: 배열 길이보다 큰 인덱스를 일부러 사용해 보고 경고 메시지나 이상한 출력이 왜 생기는지 추적합니다.
  • 완료 기준: 배열 선언·인덱싱·문자열 종료 문자를 설명하고, sizeof를 이용해 길이를 구해 반복문을 작성할 수 있으면 됩니다.

마무리

이번 글에서는 배열과 문자열로 여러 값을 한 번에 다루는 방법을 살폈습니다. 다음 글에서는 배열이 실제로 메모리 주소와 어떻게 연결되는지, 그리고 포인터가 왜 필요한지를 소개합니다.

💬 댓글

이 글에 대한 의견을 남겨주세요