티스토리 뷰

C | C++

스트링과 메모리 비교

야라바 2018. 5. 31. 20:10
728x90

☞ memcmp, strcmp, strncmp, strcoll, strxfrm


<string.h>

int memcmp(const void *mem1, const void *mem2, size_t size);
int strcmp(const char *str1, const char *str2);
int strncmp(const char *str1, const char *str2, size_t size);
int strcoll(const char *str1, const char *str2);
size_t strxfrm(char *dest, const char *src, size_t size);

많은 컴퓨터 프로그램은 비교문의 연속이라 할 정도로 코드 핵심부에는 비교문이 있기 마련입니다. 그 중에서 스트링 또는 문자열, 메모리 비교도 아주 중요한 부분입니다. 비교를 수행하는 내장 함수를 호출한 결과는 숫자를 비교하는 방식과 유사합니다. 예를 들어 if (a > b) 문장은 a - b 연산 결과가 양수일때 참이고, if (a == b) 문장은 a - b 연산 결과가 0일때 참입니다. 마찬가지로 if (a < b) 문장은 a - b 연산 결과가 음수 일때 참입니다. 이 과정을 준용해서 mem...() 또는 str...()함수의 리턴값이 양수이면 mem1 또는 str1이 크다는 의미이고 0 이면 길이와 내용이 모두 같다는 의미입니다.

memcmp()와 strncmp()의 경우에는 비교할 메모리의 크기나 최대 길이를 지정하므로 안전한 편이지만 strcmp()의 경우에는 비교 대상으로 지정하는 두 포인터가 널포인터가 아니고 포인터로 지정한 문자열 내용에 널문자로 스트링의 끝을 반드시 지정하고 있어야 합니다.

문자열을 비교하는 경우에는 언어별로 문자값의 순서가 그대로 비교에 되지 않고 언어별 코드 페이지(locale code page)가 적용됩니다. 예를 들어 문자 'A'와 'B'를 비교하는 과정은 'A'의 문자값인 0x41과 'B'의 문자값인0x42를 비교하는 것이므로 문제가 없습니다. 그렇지만 대문자 'A'와 소문자 'a'를 비교하는 경우, 영문자 'A'와 숫자 '0'을 비교하는 경우에는 이야기가 달라집니다. 대문자가 소문자 보다 큰지, 영문자가 숫자 보다 큰지를 어떻게 인식하는가의 문제가 있기 때문입니다. 이런 문제를 콜레이션(Collation) 이라고 합니다. 데이터베이스에서 정렬이나 검색의 정확도에 영향을 주는 요소 이기도 합니다. 우리가 일반적으로 사용하는 코드 체계인 아스키(ASCII)코드에서는 소문자 'a'는 0x61, 숫자 '0'은 0x30이기 때문에 문자열을 비교하면 숫자 < 대문자 < 소문자의 순서가 됩니다. 문제는 한글처럼 한중일 문자나, 유럽의 여러 언어에서 사용하는 문자를 비교하는 경우인데 강세부호가 있는 문자를 강세 부호가 없는 것과 동일하게 처리한다거나 스페인어에서 ll을 하나의 l과 동일하게 처리한다거나 하는 것을 예로 들 수 있습니다.

☞ 콜레이션 환경 설정

if ((env_locale = getenv("LC_COLLATE")) != NULL)
    init_locale("LC_COLLATE", LC_COLLATE, env_locale);
else
    init_locale("LC_COLLATE", LC_COLLATE, "");

if ((env_locale = getenv("LC_CTYPE")) != NULL)
    init_locale("LC_CTYPE", LC_CTYPE, env_locale);
else
    init_locale("LC_CTYPE", LC_CTYPE, "");



언어별 콜레이션을 감안한 문자열 비교가 필요한 경우에 사용하는 것이 strcoll()과 strxfrm() 입니다. 특정 언어에 대한 콜레이션을 적용하려면 LC_COLLATE 환경변수 설정이 필요한데 환경 변수를 설정하지 않으면 C 표준 모드로 동작하므로 이런 상태에서 strcoll()을 호출하면 strcmp()와 동일한 동작을 하는 것입니다. 언어별 콜레이션은 코드 내부에서는 setlocale()로 지정할 수 있습니다. 위의 예제는 PostgreSQL DBMS의 코드 일부로 DBMS를 기동하면서 콜레이션을 확인 및 설정하는 것을 확인할 수 있습니다. 위의 코드에서 호출하는 init_locale() 함수를 추적해보면 setlocale()로 설정하는 것과 함께 putenv()로 환경 변수에 설정하는 작업을 동시에 수행하고 있습니다.

strxfrm()은 콜레이션이 적용되고 있는 환경에서 원본 스트링 src를 콜레이션을 감안한 스트링으로 dest에 저장하는 동작을 수행합니다. strxfrm()은 변환된 스트링의 길이를 리턴합니다. 콜레이션에 따라 원본보다 작을 수도 길 수도 있으므로 확인이 필요합니다.

☞ 예제

static int hb_is_heartbeat_valid (char *host_name, char *group_id, struct sockaddr_in *from)
{
	int error;
	struct in_addr sin_addr;
	HB_NODE_ENTRY *node;

	node = hb_return_node_by_name_except_me (host_name);
	if (node == NULL)
	{
		return HB_VALID_UNIDENTIFIED_NODE;
	}

	if (strcmp (group_id, hb_Cluster->group_id) != 0)
	{
		return HB_VALID_GROUP_NAME_MISMATCH;
	}

	error = hb_hostname_to_sin_addr (host_name, &sin_addr);
	if (error == NO_ERROR)
	{
		if (memcmp ((void *) &sin_addr, (void *) &from->sin_addr, sizeof (struct in_addr)) != 0)
		{
			return HB_VALID_IP_ADDR_MISMATCH;
		}
	}
	else
	{
		return HB_VALID_CANNOT_RESOLVE_HOST;
	}

	return HB_VALID_NO_ERROR;
}

위의 예제 코드는 큐브리드 DBMS의 코드 일부로 필요에 따라 strcmp()와 memcmp()를 적절하게 사용하는 것을 참조할 수 있습니다. 단순 문자열 비교 뿐만아니라 두 구조체가 같은지를 memcmp()를 활용하여 간편하게 확인할 수 있음을 참조할 수 있습니다.




728x90

'C | C++' 카테고리의 다른 글

스트링 토큰 처리  (0) 2018.05.31
스트링 및 메모리 검색  (0) 2018.05.31
스트링 연결과 제한된 복사  (0) 2018.05.24
스트링/메모리의 복사와 이동  (0) 2018.05.24
스트링/문자 배열의 이해와 길이 구하기  (0) 2018.05.24