* strtok() 사용시 주의사항

C, C++ 언어 사용시 문자열을 자르는 처리 할 경우 주로 strtok()과 strtok_r() 함수를 사용합니다.
여기서 중요한 점은 strtok() 함수가 multi-thread 프로그램에서 오류를 유발할 수 있으므로, multi-thread 프로그램 구현 시

strtok_r() 함수를 사용해야 합니다.

 

#include <string.h>

char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);

 

strtok() 함수는 str문자열을 delim에 포함된 문자들 중에서 하나를 만나는 곳을 null-terminated 문자열로 변경하여 return 합니다. 
str 문자열이 NULL이면 그 다음 delimiter까지의 문자열을 return합니다. 

strtok_r() 함수의 saveptr은 문자열을 strtok()의 내부에서 관리하던 static 변수 대신에 외부 변수를 사용하도록 변경하여

multi-thread에 안전하도록 설계되었습니다.

* strtok() 함수 원형

char 
*strtok (char *s, const char *delim)
{
  static char *olds;
  return __strtok_r (s, delim, &olds);
}


strtok() 함수의 내부에서 static char *olds 선언 후 __strtok_r() 함수를 호출하여 해당 값을 반환하는 과정에서 *olds 는 static으로 선언되어 프로그램의 data 영역에 저장됩니다.  


즉, 최초 초기화 이후 프로그램 종료 시점까지 메모리 공간에 존재하는 전역변수의 특성을 가집니다.
따라서  strtok() 함수를  multi-thread 에서 사용하게 되면 예게치 못한 동작이 발생하게 됩니다. 


strtok_r() 과 같이  multi-thread 에서 실행시 문제가 없는 함수를 thread-safe 함수라고 하는데, 

이는 함수 내에서 전역 변수를 사용하지 않거나, 쓰레드에서 사용되는 공유자원을 lock 같은 동기화 기법으로 보호하여 공유 자원의 무결성을 보장합니다. strtok_r() 처럼 표준 함수 이름에 _r이 붙은 함수들은 thread-safe 한 함수들 입니다.

* strtok_r() 함수 원형

char *
__strtok_r (char *s, const char *delim, char **save_ptr)
{
  char *end;
  if (s == NULL)
    s = *save_ptr;
  if (*s == '\0')
    {
      *save_ptr = s;
      return NULL;
    }
  /* Scan leading delimiters.  */
  s += strspn (s, delim);
  if (*s == '\0')
    {
      *save_ptr = s;
      return NULL;
    }
  /* Find the end of the token.  */
  end = s + strcspn (s, delim);
  if (*end == '\0')
    {
      *save_ptr = end;
      return s;
    }
  /* Terminate the token and make *SAVE_PTR point past it.  */
  *end = '\0';
  *save_ptr = end + 1;
  return s;
}