툴곰코드 노트C언어 › C언어 문자열

C언어 문자열

C에는 문자열 형이 없습니다. char 배열 끝에 \0을 붙여 문자열인 척하는 것이라, 비교도 복사도 함수를 써야 합니다.

⬇ 예제 파일 받기 (.c)
C언어 · 회원가입 없음
내려받아 gcc 파일이름.c -o 실행파일 -Wall 로 컴파일한 뒤 실행합니다.
광고 영역

무엇을 하는 것인가

파이썬에는 str이 있지만 C에는 문자열 형이 없습니다. char 이름[] = "김서연";은 문자 하나하나를 배열에 담고, 맨 끝에 \0(널 문자)을 하나 더 붙인 것입니다. 이 \0이 "여기가 끝"이라는 표시입니다.

그래서 "abc"는 배열 크기가 4입니다. 눈에 보이는 글자 셋에 \0 하나가 더 들어갑니다. 배열 크기를 잡을 때 이 한 칸을 빠뜨리면 끝 표시가 없어져서, 출력할 때 엉뚱한 값이 줄줄이 딸려 나옵니다.

문자열을 ==로 비교하면 안 됩니다. 이름 == "김서연"은 내용이 아니라 주소를 비교합니다. 거의 언제나 거짓입니다. 내용을 비교하려면 <string.h>strcmp를 씁니다. strcmp는 같으면 0을 돌려주는데, 이것도 헷갈리는 자리입니다.

복사도 =로 안 됩니다. strcpy(대상, 원본)을 씁니다. 이때 대상 배열이 원본보다 작으면 넘쳐서 남의 메모리를 덮어씁니다. 크기를 제한하는 strncpysnprintf를 쓰는 편이 안전합니다.

입력받을 때 scanf("%s", 이름)공백에서 끊기고 길이 제한도 없습니다. 한 줄을 통째로 받으려면 fgets를 쓰세요. fgets는 줄바꿈까지 같이 담으므로 그 한 글자를 떼어내야 합니다.

예제 코드

아래 코드가 위 버튼으로 받는 파일과 같은 내용입니다. 통째로 복사해 실행해 보고, 값을 바꿔 가며 결과가 어떻게 달라지는지 보는 편이 읽기만 하는 것보다 훨씬 빨리 익습니다.

/* 문자열 — char 배열 끝에 \0 을 붙인 것
   컴파일:  gcc c-string.c -o str -Wall                  */

#include <stdio.h>
#include <string.h>        /* strcmp, strcpy, strlen 을 쓰려면 필요 */

int main(void)
{
    /* 끝에 \0 이 자동으로 붙습니다. 그래서 크기는 글자 수 + 1 입니다. */
    char 이름[20] = "김서연";
    char 인사말[] = "abc";

    printf("이름: %s\n", 이름);
    printf("strlen(\"abc\") = %zu\n", strlen(인사말));   /* 3  — \0 은 안 셉니다 */
    printf("sizeof(\"abc\") = %zu\n", sizeof(인사말));   /* 4  — \0 까지 셉니다 */

    /* 배열이므로 한 칸씩 꺼낼 수 있습니다. 다만 char 는 "글자" 가 아니라
       "1바이트" 입니다. 영문·숫자는 한 글자가 1바이트라 그대로 나옵니다. */
    char 영문[] = "Hello";
    for (size_t i = 0; i < strlen(영문); i++) {
        printf("[%c]", 영문[i]);          /* [H][e][l][l][o] */
    }
    printf("\n");

    /* 한글은 한 글자가 UTF-8 로 3바이트입니다. 그래서 이렇게 하면 깨집니다.
       strlen("김서연") 이 3 이 아니라 9 로 나오는 것도 같은 이유입니다.     */
    printf("strlen(이름) = %zu  (글자 수가 아니라 바이트 수)\n", strlen(이름));

    /* ── 비교: == 는 주소를 봅니다. 내용은 strcmp ── */
    char 입력[20] = "김서연";

    if (strcmp(이름, 입력) == 0) {          /* 같으면 0 */
        printf("같은 이름입니다.\n");
    } else {
        printf("다른 이름입니다.\n");
    }

    printf("사전순 비교: %d\n", strcmp("apple", "banana"));   /* 음수 */

    /* ── 복사: = 로는 안 됩니다 ── */
    char 사본[20];
    /* 사본 = 이름;   ← 컴파일 오류 */
    strcpy(사본, 이름);
    printf("사본: %s\n", 사본);

    /* strcpy 는 대상 배열이 작으면 그냥 넘쳐서 남의 메모리를 덮어씁니다.
       snprintf 는 sizeof 로 한계를 알려 주므로, 자리가 모자라면 넘치는 대신
       들어갈 만큼만 담고 끝에 \0 을 붙입니다. 이쪽이 안전합니다.          */
    char 칸[60];
    snprintf(칸, sizeof(칸), "%s님 환영합니다", 이름);
    printf("안전하게 담기: %s\n", 칸);

    /* 이어 붙이기 */
    char 문장[40] = "안녕하세요, ";
    strcat(문장, 이름);
    strcat(문장, "님");
    printf("%s\n", 문장);

    /* ── 한 줄 입력은 fgets. scanf("%s") 는 공백에서 끊깁니다 ── */
    char 한줄[50];
    printf("한 줄 입력: ");
    if (fgets(한줄, sizeof(한줄), stdin) != NULL) {
        한줄[strcspn(한줄, "\n")] = '\0';    /* 줄 끝 엔터 떼어내기 */
        printf("받은 줄: [%s] (%zu글자)\n", 한줄, strlen(한줄));
    }

    /* char 배열은 고칠 수 있지만, char * 가 가리키는 문자열은 못 고칩니다 */
    char 고칠수있음[] = "abc";
    고칠수있음[0] = 'A';
    printf("고친 결과: %s\n", 고칠수있음);

    const char *못고침 = "abc";       /* const 를 붙여 실수를 막습니다 */
    printf("읽기만: %s\n", 못고침);

    return 0;
}

기억할 것

광고 영역

자주 묻는 것

strcmp 가 같은데 0 을 주는 게 이상합니다

strcmp는 "다른 정도"를 돌려주도록 만들어졌습니다. 앞 문자열이 사전순으로 뒤면 음수, 앞이면 양수, 같으면 차이가 없으니 0입니다. 그래서 조건은 if (strcmp(a, b) == 0)가 됩니다. 정렬에도 그대로 쓸 수 있어서 이렇게 만든 것입니다.

출력했더니 뒤에 이상한 글자가 붙어 나옵니다

\0이 없거나 덮여 쓰인 것입니다. printf("%s")\0을 만날 때까지 계속 출력하기 때문에, 끝 표시가 없으면 옆자리 메모리까지 읽습니다. 배열 크기를 글자 수 + 1 이상으로 잡았는지 확인하세요.

fgets 로 받으면 뒤에 줄바꿈이 붙습니다

fgets는 엔터까지 같이 담습니다. 이름[strcspn(이름, "\n")] = '\0'; 한 줄로 떼어낼 수 있습니다. 이 관용구는 그냥 외워 두면 편합니다.

한글이 한 글자씩 안 잘립니다

char는 글자가 아니라 1바이트이기 때문입니다. UTF-8 에서 한글 한 글자는 3바이트라, 이름[0]은 "김"이 아니라 그 첫 바이트입니다. 그걸 %c로 찍으면 깨져 보입니다. 입문 단계에서는 한글 문자열은 통째로 다루고 자르지 않는 것이 안전합니다. 굳이 한 글자씩 다뤄야 하면 <wchar.h>의 넓은 문자를 씁니다.

char 이름[] 와 char *이름 은 다른가요

다릅니다. char 이름[] = "김";복사본을 만들어 담아 고칠 수 있습니다. char *이름 = "김";읽기 전용 자리를 가리키기만 해서, 고치려 들면 프로그램이 죽습니다. 고칠 문자열은 배열로 잡으세요.

이어서 볼 글