[C/C++] strcmp vs strncmp: 문자열 비교 함수의 정확한 사용법과 핵심 차이점 (예제 포함)

안녕하세요! 코딩 세계에서 문자열을 다루는 것은 매우 흔한 일입니다. 그중에서도 두 문자열이 같은지, 다르다면 어떤 부분이 다른지 확인하는 작업은 필수적인데요. 오늘은 C언어와 C++에서 문자열을 비교하는 데 사용되는 표준 라이브러리 함수인 strcmpstrncmp에 대해 자세히 알아보겠습니다.

1. 문자열 비교 함수의 필요성

프로그램을 작성하다 보면 사용자 입력을 확인하거나, 특정 조건에 따라 다른 동작을 수행해야 할 때 문자열을 비교해야 하는 상황이 자주 발생합니다.

예를 들어, 아이디나 비밀번호가 일치하는지, 특정 명령어가 입력되었는지 등을 판별할 때 문자열 비교가 사용됩니다. C언어에서 문자열은 단순히 == 연산자로 비교할 수 없습니다. (이는 포인터 주소값을 비교하게 됩니다.) 따라서 문자열의 내용 자체를 비교하기 위한 전용 함수가 필요하며, strcmpstrncmp가 바로 그 역할을 합니다.

2. strcmp 함수: 문자열 전체 비교

strcmp 함수는 두 개의 문자열을 처음부터 끝까지 비교합니다.

2.1. 정의 및 헤더파일

  • C언어 헤더파일: #include <string.h>
  • C++ 헤더파일: #include <cstring> (또는 <string.h>)
  • 함수 원형: int strcmp(const char *str1, const char *str2);

2.2. 매개변수

  • const char *str1: 비교할 첫 번째 문자열입니다.
  • const char *str2: 비교할 두 번째 문자열입니다. *두 매개변수 모두 const로 선언되어 함수 내에서 문자열이 변경되지 않음을 보장합니다.

2.3. 반환값

strcmp 함수는 두 문자열을 사전 순서(ASCII 코드 값 기준)로 비교하여 다음과 같은 정수 값을 반환합니다.

  • 0: str1str2의 내용이 완전히 같을 경우
  • 음수 (예: -1): str1str2보다 사전적으로 앞에 나올 경우 (예: strcmp("apple", "apply")는 'e'가 'y'보다 작으므로 음수 반환)
  • 양수 (예: 1): str1str2보다 사전적으로 뒤에 나올 경우 (예: strcmp("banana", "apple")는 'b'가 'a'보다 크므로 양수 반환)

2.4. 동작 원리

strcmp는 각 문자열의 첫 번째 문자부터 시작하여 문자를 하나씩 비교합니다.

  1. 두 문자가 다르면, 해당 문자의 ASCII 코드 값 차이에 따라 양수 또는 음수를 반환하고 비교를 종료합니다.
  2. 두 문자가 같으면, 다음 문자로 이동하여 비교를 계속합니다.
  3. 두 문자열 중 하나가 널 문자(\0)에 도달하면 비교가 종료됩니다.
    • 만약 한 문자열은 널 문자에 도달했고 다른 문자열은 아직 문자가 남아있다면, 널 문자가 아닌 문자를 가진 문자열이 더 "크다"고 판단됩니다. (널 문자의 ASCII 값은 0이므로 다른 어떤 출력 가능 문자보다 작습니다.)
    • 두 문자열이 동시에 널 문자에 도달하면 두 문자열은 같은 것으로 판단되어 0을 반환합니다.

주의: strcmp 함수는 대소문자를 구분합니다. 예를 들어, "Apple"과 "apple"은 다른 문자열로 취급됩니다.

3. strncmp 함수: 지정된 길이만큼 문자열 비교

strncmp 함수는 strcmp와 유사하지만, 추가적으로 비교할 문자의 최대 개수를 지정할 수 있다는 차이점이 있습니다.

3.1. 정의 및 헤더파일

  • C언어 헤더파일: #include <string.h>
  • C++ 헤더파일: #include <cstring> (또는 <string.h>)
  • 함수 원형: int strncmp(const char *str1, const char *str2, size_t n);

3.2. 매개변수

  • const char *str1: 비교할 첫 번째 문자열입니다.
  • const char *str2: 비교할 두 번째 문자열입니다.
  • size_t n: 비교할 최대 문자 수입니다. size_t는 부호 없는 정수 타입입니다.

3.3. 반환값

strncmp 함수의 반환값은 strcmp와 동일한 규칙을 따릅니다. 단, 비교는 최대 n개의 문자까지만 이루어집니다.

  • 0: str1str2의 처음 n개 문자가 같거나, n개 문자를 비교하기 전에 두 문자열 모두 널 문자에 도달하여 동일한 경우
  • 음수: 처음 n개 문자 내에서 str1str2보다 사전적으로 앞에 나올 경우
  • 양수: 처음 n개 문자 내에서 str1str2보다 사전적으로 뒤에 나올 경우

3.4. n 매개변수의 특성

  • 만약 n이 0이면, strncmp는 아무 문자도 비교하지 않으므로 항상 0을 반환합니다.
  • 만약 n이 비교 대상 문자열들의 실제 길이보다 크다면, strncmpstrcmp와 동일하게 문자열 전체를 비교하는 것처럼 동작합니다 (즉, 널 문자 \0를 만날 때까지 비교).
  • n에 음수 값을 전달하는 것은 size_t가 부호 없는 타입이므로 정의되지 않은 동작(undefined behavior)을 유발할 수 있습니다. 일반적으로 매우 큰 양수로 해석되어 문자열 전체를 비교하게 될 가능성이 높지만, 이식성을 위해 항상 양수 값을 사용해야 합니다.

4. strcmpstrncmp 사용 예제

이제 실제 코드를 통해 두 함수의 사용법을 살펴보겠습니다.

4.1. strcmp 예제

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

int main() {
    const char *s1 = "BlockDMask";
    const char *s2 = "Block";
    const char *s3 = "BlockDMask";
    const char *s4 = "BlockFMask"; // D보다 F가 큼
    const char *s5 = "BlockAMask"; // D보다 A가 작음

    printf("strcmp(s1, s2) (\"%s\", \"%s\"): %d\n", s1, s2, strcmp(s1, s2));
    // s1이 "BlockDMask", s2가 "Block"일 때
    // "Block"까지는 동일. s1의 'D'와 s2의 '\0' 비교. 'D' > '\0' 이므로 양수.

    printf("strcmp(s1, s3) (\"%s\", \"%s\"): %d\n", s1, s3, strcmp(s1, s3));
    // s1과 s3는 완전히 동일하므로 0.

    printf("strcmp(s1, s4) (\"%s\", \"%s\"): %d\n", s1, s4, strcmp(s1, s4));
    // 'D' < 'F' 이므로 음수.

    printf("strcmp(s1, s5) (\"%s\", \"%s\"): %d\n", s1, s5, strcmp(s1, s5));
    // 'D' > 'A' 이므로 양수.

    if (strcmp(s1, s3) == 0) {
        printf("\"%s\"와 \"%s\"는 같습니다.\n", s1, s3);
    } else {
        printf("\"%s\"와 \"%s\"는 다릅니다.\n", s1, s3);
    }

    return 0;
}

예상 결과:

strcmp(s1, s2) ("BlockDMask", "Block"): 1  (환경에 따라 다른 양수일 수 있음)
strcmp(s1, s3) ("BlockDMask", "BlockDMask"): 0
strcmp(s1, s4) ("BlockDMask", "BlockFMask"): -2 (환경에 따라 다른 음수일 수 있음, D-F)
strcmp(s1, s5) ("BlockDMask", "BlockAMask"): 3  (환경에 따라 다른 양수일 수 있음, D-A)
"BlockDMask"와 "BlockDMask"는 같습니다.

strcmp(s1, s2)의 경우, "Block" 다음 문자인 s1의 'D'와 s2의 널 문자 \0가 비교됩니다. 'D'의 ASCII 값이 \0의 ASCII 값(0)보다 크므로 양수가 반환됩니다.


4.2. strncmp 예제

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

int main() {
    char str1[] = "BlockDMask";
    char str2[] = "Block";
    char str3[] = "BlockAAAAA";

    // n = 0: 비교 안함, 항상 0
    printf("strncmp(str1, str2, 0)  (\"%s\", \"%s\"): %d\n", str1, str2, strncmp(str1, str2, 0));

    // n = 5: "Block" vs "Block" -> 같음, 0
    printf("strncmp(str1, str2, 5)  (\"%s\", \"%s\"): %d\n", str1, str2, strncmp(str1, str2, 5));

    // n = 6: "BlockD" vs "Block\0" -> 'D' > '\0', 양수
    printf("strncmp(str1, str2, 6)  (\"%s\", \"%s\"): %d\n", str1, str2, strncmp(str1, str2, 6));
    
    // n = 1000 (문자열 길이보다 큼): 전체 비교, 'D' > '\0', 양수
    printf("strncmp(str1, str2, 1000) (\"%s\", \"%s\"): %d\n", str1, str2, strncmp(str1, str2, 1000));

    // n = 5: "Block" vs "Block" -> 같음, 0
    printf("strncmp(str1, str3, 5)  (\"%s\", \"%s\"): %d\n", str1, str3, strncmp(str1, str3, 5));
    
    // n = 6: "BlockD" vs "BlockA" -> 'D' > 'A', 양수
    printf("strncmp(str1, str3, 6)  (\"%s\", \"%s\"): %d\n", str1, str3, strncmp(str1, str3, 6));

    return 0;
}

예상 결과:

strncmp(str1, str2, 0)  ("BlockDMask", "Block"): 0
strncmp(str1, str2, 5)  ("BlockDMask", "Block"): 0
strncmp(str1, str2, 6)  ("BlockDMask", "Block"): 68 (환경에 따라 다름, 'D'의 ASCII 값)
strncmp(str1, str2, 1000) ("BlockDMask", "Block"): 68 (환경에 따라 다름)
strncmp(str1, str3, 5)  ("BlockDMask", "BlockAAAAA"): 0
strncmp(str1, str3, 6)  ("BlockDMask", "BlockAAAAA"): 3  (환경에 따라 다름, 'D'-'A')

5. 핵심 요약 및 주의사항

  1. strcmp: 두 문자열 전체를 비교합니다.
  2. strncmp: 두 문자열의 처음 n개 문자까지만 비교합니다.
  3. 반환값:
    • 0: 두 문자열(또는 비교된 부분)이 같음
    • 음수: 첫 번째 문자열이 두 번째 문자열보다 사전적으로 앞에 옴
    • 양수: 첫 번째 문자열이 두 번째 문자열보다 사전적으로 뒤에 옴
  4. 널 문자(\0): 문자열의 끝을 의미하며, 비교 과정에서 중요한 역할을 합니다. 모든 출력 가능 문자보다 ASCII 값이 작습니다.
  5. 대소문자 구분: 두 함수 모두 대소문자를 구분합니다. 대소문자를 구분하지 않는 비교를 원한다면, strcasecmp (POSIX) 또는 _stricmp (Windows) 같은 비표준 함수를 사용하거나, 문자열을 모두 대문자 또는 소문자로 변환한 후 비교해야 합니다.
  6. size_t n: strncmpn은 부호 없는 정수이므로, 음수 값을 전달하지 않도록 주의해야 합니다.

6. 퀴즈!

다음 코드의 결과는 무엇일까요? 그리고 그 이유는 무엇일까요?

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

int main() {
    const char *s1 = "BlockD\0Mask"; // 문자열 중간에 널 문자!
    const char *s2 = "BlockD\0AAAA";

    int result = strcmp(s1, s2);
    printf("strcmp(\"BlockD\\0Mask\", \"BlockD\\0AAAA\") = %d\n", result);
    
    return 0;
}

정답 및 해설: 결과는 0입니다. strcmp 함수는 문자열을 비교하다가 널 문자(\0)를 만나면 그곳을 문자열의 끝으로 간주합니다. s1s2 모두 "BlockD" 다음에 널 문자가 오므로, strcmp는 "BlockD"까지만 비교하고 두 문자열이 같다고 판단하여 0을 반환합니다. 널 문자 뒤에 오는 "Mask"나 "AAAA" 부분은 비교 대상에 포함되지 않습니다.


strcmpstrncmp 함수는 C/C++ 프로그래밍에서 문자열을 다룰 때 기본적이면서도 매우 유용한 함수들입니다. 이 함수들의 동작 원리와 반환값을 정확히 이해하고 사용한다면, 더욱 견고하고 정확한 프로그램을 작성하는 데 큰 도움이 될 것입니다.