PS/자주 하는 답변

문자열의 끝까지만 봐야 합니다.

djm03178 2023. 1. 13. 06:37

C/C++에서 문자열이란 초보자가 다루기가 상당히 까다로운 편에 속합니다. 정확한 사용법을 숙지하지 못하고 쓰다가 실수를 하는 일도 굉장히 잦습니다. 대표적인 사례가 바로 문자열의 끝과 관련한 것들입니다.

 

문자열의 끝에는 항상 널 문자가 들어갑니다. 글에서 다루었듯이 일반적인 char로 나타낸 문자열의 끝은 항상 널 문자 ('\0')입니다. C++의 std::string과 같은 경우에도 문자열의 마지막 글자의 다음 인덱스에 접근할 경우 널 문자를 반환할 것이 표준에 명시되어 있습니다. 다시 강조하지만 널 문자는 문자열로서의 마지막을 나타냅니다. 달리 말하면 널 문자를 넘어선 부분은 문자열의 일부가 아닙니다.

 

흔히 문자열을 입력받기 위해 다음과 같은 고정된 크기의 배열을 선언하고 입력을 받습니다.

char str[1000005];
scanf("%s", str); // 또는 cin >> str;

그 다음 str에 저장된 문자열을 한 문자씩 확인하기 위해 여러 방법을 사용할 수 있습니다. 다음 중 어느 것들이 올바른 방법일까요?

// 1
for (int i = 0; i < 1000005; i++)
    putchar(str[i]);

// 2
for (int i = 0; i < strlen(str); i++)
    putchar(str[i]);

// 3
int len = strlen(str);
for (int i = 0; i < len; i++)
    putchar(str[i]);

// 4
for (int i = 0; i < sizeof(str); i++)
    putchar(str[i]);

// 5
for (int i = 0; str[i] != '\0'; i++)
    putchar(str[i]);

// 6
for (int i = 0; str[i]; i++)
    putchar(str[i]);

정답은 다음과 같습니다.

  • 1번: 1000005는 문자열의 길이가 아니라 배열의 크기입니다. 즉, 문자열의 길이가 그보다 짧다면 널 문자를 넘어선 공간에 접근하게 됩니다. 이 부분은 scanf가 입력받을 때 아예 건드리지도 않았기 때문에 str가 지역 변수라면 아예 초기화도 되어 있지 않을 것이고, 전역 변수라면 전부 0의 값 (널 문자)로 초기화되어 있을 것이고, 이전에 다른 문자열을 입력받은 적이 있다면 그 내용이 그대로 남아있을 수도 있습니다. 간과하기 쉬운데, 널 문자는 많은 환경에서 공백처럼 투명하게 출력되거나 아예 아무것도 출력되지 않는 것처럼 보입니다. 하지만 어떤 문제도 널 문자의 출력을 요구하지 않기 때문에 실제로 널 문자를 출력한다면 무조건 오답을 받습니다.
  • 2번: strlen 함수는 문자열의 길이를 구해주기 때문에 방법 자체는 올바른 것이 맞습니다. 하지만 반복문의 조건문 안에 strlen을 넣지 마세요. 글에서 언급한 바와 같이 이는 매우 비효율적인 방법입니다. 자세한 것은 해당 링크의 글을 참고하세요.
  • 3번: 2번의 링크의 글에서 언급한 바와 같이 길이를 미리 구해두었기 때문에 괜찮은 방법입니다.
  • 4번: sizeof는 문자열의 길이를 구해주는 함수가 아니라 자료형의 크기를 구하는 연산자입니다. str의 자료형은 크기가 1000005인 char형 배열이므로, sizeof(str)는 입력받은 내용이 무엇이든지 관계 없이 무조건 1000005의 값을 가집니다. 즉, 이는 1번과 동일한 문제점을 안고 있습니다.
  • 5번, 6번: 정확하게 문자열의 끝에 도달할 때까지 보는 올바른 방법들입니다. str[i]가 널 문자에 도달할 때까지만 루프를 돌고 그 이후에 빠져나가기 때문에 문자열의 끝을 넘어서 보지 않습니다. 널 문자는 0의 값을 가지므로 그냥 str[i]라고만 써도 그 자체로 널 문자가 아니라는 조건식이 될 수 있습니다.