Core Programming/C for Systems Engineering

C언어 배열과 문자열 기초: 메모리 구조부터 핵심 문자열 함수 완벽 정리

임베디드 친구 2024. 12. 13. 13:37
반응형

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

Generated by Gemini AI.

📌 핵심 요약 3줄

  • 연속된 메모리 공간: 배열은 같은 자료형의 데이터들을 메모리상에 빈틈없이 연속적으로 이어 붙여 저장하는 효율적인 자료구조입니다.
  • 문자열과 널 문자(\0): C 언어에서 문자열은 문자 배열의 형태를 가지며, 컴퓨터가 문자열의 끝을 식별할 수 있도록 항상 맨 마지막에 '널 문자'를 포함해야 합니다.
  • 라이브러리를 통한 확장: <string.h> 헤더가 제공하는 표준 함수들을 활용하면 복잡한 문자열 복사, 비교, 길이 계산을 단 한 줄로 간단하게 처리할 수 있습니다.

1. 같은 데이터의 일렬 종대, 배열 (Array)

배열은 쉽게 말해 방 번호(인덱스)가 매겨진 아파트라고 생각하면 편합니다. 단, 같은 크기의 방에 같은 종류의 자료형만 들어갈 수 있다는 규칙이 있습니다.

배열 용어 기능 및 특징 설명 주의해야 할 점
선언 (Declaration) 자료형, 배열 이름, 사용할 칸의 개수를 대괄호[]안에 명시합니다. 크기는 반드시 명확한 상수나 숫자로 지정해야 합니다.
초기화 (Initialization) 중괄호{}를 사용해 선언과 동시에 공간에 값을 채워 넣습니다. 초기화 값을 채울 때 배열 크기를 생략하면 자동으로 크기가 잡힙니다.
인덱스 (Index) 배열의 특정 칸에 접근하는 방 번호입니다. 방 번호는 1이 아니라 0부터 시작한다는 점을 명심해야 합니다.

💻 1차원 배열 및 다차원 배열 선언과 접근 예제

C
 
#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바이트의 공간이 필요합니다.

💻 문자열 입출력 예제

C
 
#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을 반환합니다. 저울질하여 글자 비교하기

💻 문자열 내장 함수 활용 및 뒤집기 실습 예제

C
 
#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)

  1. 배열의 크기를 동적으로 구하려면 sizeof를 나누세요: 배열의 전체 방 개수를 코드 내에서 유연하게 계산하고 싶을 때는 sizeof(배열이름) / sizeof(배열[0]) 수식을 쓰면 됩니다. 배열의 전체 바이트 크기에서 방 딱 한 칸의 자료형 크기를 나누는 원리인데, 데이터 개수가 바뀌어도 반복문 조건을 수정할 필요가 없어서 아주 유용합니다.
  2. 공백이 포함된 문자열 입력은 fgets를 쓰세요: scanf("%s", 변수)는 편리하지만 스페이스바(공백)나 탭 문자를 만나는 순간 입력이 끝난 것으로 인식합니다. "Hong Gil Dong"처럼 중간에 공백이 들어간 문자열을 통째로 안전하게 입력받고 싶다면 문장 한 줄을 통째로 읽어오는 fgets() 함수를 사용하는 것을 권장합니다.
  3. 문자열 비교 시 == 기호는 절대 금물입니다: 숫자형 변수들과 달리 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"); 처럼 복사 함수를 통과시켜야 합니다.

🔚 맺음말

이번 시간에는 수많은 데이터 정리를 한 번에 해결해 주는 배열과, 대화형 프로그램의 기초가 되는 문자열의 핵심 메커니즘을 꼼꼼하게 짚어보았습니다. 데이터들이 메모리에 나란히 연속적으로 배치되어 숨 쉬고 있다는 물리적인 공간 개념을 머릿속에 그릴 수 있다면 이번 단원을 아주 훌륭하게 완수하신 겁니다.

오늘 내용 중 헷갈리는 부분이 있다면 언제든 소프트웨어 공장 댓글 창을 적극적으로 활용해 주세요!

반응형