C, C++ 언어

C언어 기초 15. 문자들의 배열, 문자열

게임플밍마스터 2025. 10. 23. 17:30

안녕하세요. c언어 기초 블로그를 작성하고 있는 게임플밍마스터 입니다. 

지난 시간에는 포인터라는 다소 추상적인 개념을 맛보았으니, 이제 포인터를 활용하여 우리에게 훨씬 더 실용적이고 친숙한 '문자열', 즉 텍스트 데이터를 다루는 방법을 배워보겠습니다. 이번 장을 배우고 나면 사용자로부터 이름, 문장 등을 입력받아 처리하는 진짜 프로그램다운 프로그램을 만들 수 있게 될 것입니다.


우리는 지금까지 숫자(int, double)와 단 한 개의 문자(char)를 다루는 법을 배웠습니다. 하지만 "Hello", "홍길동"과 같이 여러 문자가 모인 문자열(String) 은 어떻게 저장하고 다룰 수 있을까요?

Java나 Python, C++ 같은 다른 언어들은 string이라는 편리한 자료형을 제공하지만, C언어는 조금 다릅니다. C언어에서 문자열은 '문자들의 배열' 이라는 방식으로 다루어집니다. 그리고 한 가지 매우 중요한 약속이 있습니다.

1. C언어의 약속 - 널 문자 \0

C언어에서 문자열은 char형 배열에 문자들이 차례대로 저장되고, 문자열의 끝을 알리기 위해 마지막에 '널 문자(Null Terminator)' 라고 불리는 '\0' 이 들어가는 형태로 정의됩니다.

예를 들어, "cat"이라는 문자열은 메모리에 다음과 같이 저장됩니다.

 

| 'c'  | 'a'  |  't'  | '\0' |

 

분명 글자는 3개인데, 실제로는 마지막에 '\0'이 추가되어 총 4개의 공간(4바이트)을 차지합니다. printf나 다른 문자열 처리 함수들은 이 '\0'을 만날 때까지가 하나의 문자열이라고 인식하고 작업을 수행합니다. 이 '\0'이 없다면, C언어는 어디까지가 문자열인지 알 수 없어 메모리의 엉뚱한 부분까지 계속 읽어 들이는 심각한 오류를 발생시킵니다.

여기서 중요한 핵심은 널문자는 문자열의 끝을 나타내는 수단이라는 사실을 이해하면 됩니다. 

널문자는 \0 로 표기하며 문자이므로 ' ' 안에 표기하는것이 원칙입니다. 

문자열은 " " 안에 표기하는 것이 원칙이구요.

 

2. 문자열 변수 만들고 초기화하기

문자열을 저장할 char 배열을 선언하고 초기화하는 방법은 두 가지입니다.

 

방법 1: 배열 초기화 방식 (원리 이해용)

char 배열에 각 문자와 \0을 직접 넣어주는 방식입니다. 매우 번거롭지만 원리를 이해하기에는 좋습니다.

 

char str[4] = {'c', 'a', 't', '\0'};

 

방법 2: 큰따옴표를 이용한 초기화 (실제 사용 방식)

큰따옴표(" ")로 문자열을 감싸서 초기화하면, 컴파일러가 알아서 문자들을 배열에 넣고 마지막에 \0을 자동으로 추가해 줍니다. 훨씬 편리하며, 실제로 이 방법을 사용합니다.

char str1[10] = "hello"; // 크기가 10인 배열에 "hello"를 저장. 남는 공간은 비어있음.
char str2[] = "world";  // 초기값의 길이에 맞춰 배열 크기가 자동으로 6으로 결정됨 ('w' 'o' 'r' 'l' 'd' '\\0')

 

3. 문자열 입출력

 

출력: printf 와 %s

printf 함수에서 %s 서식 지정자를 사용하면, 해당 char 배열의 시작 주소부터 \0을 만날 때까지 모든 문자를 출력해 줍니다.

printf("인사말: %s\n", str1);

 

입력 1: scanf 와 %s (주의가 필요!)

scanf로도 문자열을 입력 받을 수 있습니다. 한 가지 중요한 점은, 배열의 이름은 그 자체로 배열의 시작 '주소'를 의미하기 때문에 & 기호를 붙이지 않는다는 것입니다.

 

scanf("%s", str1); // &str1이 아님!

 

하지만 scanf는 두 가지 큰 단점이 있습니다:

  1. 공백 문자(스페이스, 탭, 엔터)를 만나면 입력이 끝난 것으로 간주합니다. "Hong Gildong"을 입력해도 "Hong"만 저장됩니다.
  2. 배열의 크기를 확인하지 않습니다. char str[5] 배열에 100글자를 입력하면, 배열의 경계를 넘어 다른 메모리 공간을 침범하는 '버퍼 오버플로우' 라는 심각한 보안 문제를 일으킬 수 있습니다.

입력 2: fgets 함수 (안전한 방법)

이런 단점을 보완하기 위해, 문자열을 입력받을 때는 fgets 함수를 사용하는 것이 훨씬 안전하고 좋습니다.

 

fgets(저장할_배열, 최대_입력_크기, stdin);

 

  • stdin 은 'Standard Input', 즉 표준 입력(키보드)을 의미합니다.
  • 최대_입력_크기 를 지정할 수 있어 버퍼 오버플로우를 막아줍니다. 보통 sizeof(배열이름)을 사용합니다.
  • fgets는 한 줄 전체를 읽어오며, 사용자가 누른 Enter키(\n)까지 함께 저장합니다.
#include <stdio.h>

int main(void) 
{
    char greeting[30];
    printf("인사말을 입력하세요 (29자 이내): ");
    fgets(greeting, sizeof(greeting), stdin);

    printf("입력하신 인사말: %s", greeting); // fgets는 \\n까지 저장해서 줄바꿈이 두 번 될 수 있음
    return 0;
}

 

4. 문자열을 다루는 연장통 - <string.h> 라이브러리

C언어는 문자열을 복사하거나, 길이를 재거나, 두 문자열을 비교하는 등의 유용한 함수들을 string.h라는 헤더 파일에 모아두었습니다. 이 함수들을 사용하려면 코드 상단에 #include <string.h>를 추가해야 합니다.

  • strlen(문자열) : 문자열 길이 구하기
    \0을 제외한 순수한 글자의 개수를 반환합니다.
    int len = strlen("hello"); // len에는 5가 저장됨
  • strcpy(복사받을_배열, 원본_문자열) : 문자열 복사하기
    strcpy는 'string copy'의 약자입니다. char name[10]; name = "홍길동"; 과 같은 대입은 불가능하므로, 복사 시에는 반드시 이 함수를 사용해야 합니다.
    char dest[20];
    strcpy(dest, "hello world");
  • strcmp(문자열1, 문자열2) : 문자열 비교하기
    'string compare'의 약자입니다. 두 문자열의 내용이 같은지 비교합니다. ( if (str1 == str2) 와 같은 비교는 불가능!)
    • 두 문자열이 완전히 같으면 0을 반환합니다.
    • 문자열1이 사전 순으로 앞서면 음수를, 뒤에 있으면 양수를 반환합니다. if (strcmp(str1, "admin") == 0) { // 두 문자열이 같다면... }

 

5. [도전! 프로그래밍] 간단한 로그인 프로그램

문자열 처리 함수를 연습하기에 좋은 로그인 프로그램을 만들어 봅시다.

요구사항:

  1. 프로그램 코드 안에 올바른 아이디와 비밀번호를 char 배열로 미리 저장해 둡니다. (예: char correct_id[] = "my_id";)
  2. 사용자에게 아이디와 비밀번호를 입력받습니다.
  3. strcmp 함수를 사용하여 사용자가 입력한 값과 코드 안에 저장된 값이 모두 일치하는지 확인합니다.
  4. 결과에 따라 "로그인 성공!" 또는 "아이디 또는 비밀번호가 틀렸습니다." 라는 메시지를 출력합니다.

이번 장 정리

  • C언어의 문자열은 char 배열이며, 반드시 끝에 널 문자(\0) 가 있어야 합니다.
  • 문자열 초기화는 " " (큰따옴표)를 사용하는 것이 편리하며, 이 경우 \0은 자동으로 추가됩니다.
  • 안전한 문자열 입력을 위해서는 scanf보다 fgets 함수를 사용하는 것이 좋습니다.
  • 문자열의 길이 계산(strlen), 복사(strcpy), 비교(strcmp) 등은 <string.h> 헤더에 있는 표준 함수를 사용해야 합니다.
  • 두 문자열의 내용이 같은지 비교할 때는 ==가 아닌 strcmp(str1, str2) == 0 형태로 확인해야 합니다.

이제 여러분은 숫자뿐만 아니라 텍스트 데이터까지 자유롭게 다룰 수 있게 되었습니다. 다음 장에서는 이름, 학번, 학점처럼 서로 다른 종류의 데이터들을 하나의 의미 있는 단위로 묶어주는 '구조체'에 대해 배우며, 더 복잡한 데이터를 체계적으로 관리하는 법을 익혀보겠습니다.

여기까지 이해가 잘 되는 사람은 능력자입니다.  이해가 잘 되지 않는 부분이 있는 것이 일반적인 사람입니다. 

능력자들은 능력을 잘 즐기면 되구요. 일반적인 사람들은 열심히 노력하면 됩니다. 필자도 일반적인 사람 중에 하나였습니다. 

모두 화이팅해서 열심히 공부하구요. 궁금한건 언제든 질문해주세요