C-Language/String Comparison

C String Comparison

Soul-Learner 2017. 1. 22. 21:38

C 프로그래밍, 문자열 비교시 주의할 점

C언의 문자열은 포인터를 사용한 선언문자배열을 사용한 선언으로 구분해 볼 수 있다. 포인터를 사용하여 선언된 문자열은 literal pool에 저장되는데, literal pool에는 문자열이 저장될 때 내용이 동일한 문자열을 절대로 중복되어 저장되지 않는 특징을 가진다. 

literal pool에서는 동일한 문자열이 중복되어 생성되지 않고 이미 저장된 문자열의 주소가 중복 선언되는 문자열 변수에 그대로 전달되므로 동등비교연산자(==)를 이용해서 주소를 비교해도 동일한 결과가 나오고 strcmp()를 이용해서 내용을 비교해도 동일한 결과(0)가 나온다.

그러나 malloc() 등 동적으로 생성된 문자열의 경우에는 동일한 문자열일지라도 중복되어 Heap Memory에 생성되므로 내용이 동일한 문자열도 그 주소가 서로 다르다. 그러나 strcmp()로 비교했을 때 내용이 동일한 문자열은 동일하다는 결과(0)가 나온다.

문자배열을 사용한 문자열의 선언은 literal pool에 생성되지 않으므로 문자배열을 사용하여 동일한 문자열을 중복 선언하더라도 해당 문자열의 주소를 서로 다르다.

그러므로 항상 실수하지 않고 문자열의 내용을 제대로 비교하기 위해서는 비교연산자(==)보다strcmp()와 같은 문자열 비교 함수를 사용하는 것이 바람직하다고 할 수 있다.

아래의 내용은 MSVC를 사용하여 테스트한 것이다

#include <stdio.h>
#include <string.h> // strlen(), strcpy(), strcmp()

#pragma warning(disable:4996)

int main()
{
	char* str1 = "Hello"; // string pool에 저장됨
	char* str2 = "Hello"; // string pool에 동일한 문자열은 중복생성 되지 않음

	printf("str1==str2:%s \n", str1 == str2 ? "같다" : "다르다");    // 같다
	printf("strcmp(str1,str2):%s \n", strcmp(str1, str2) == 0 ? "같다" : "다르다"); //같다

	// malloc()을 이용하면 동적 메모리(Heap)에 할당됨
	char* str3 = (char*)malloc(strlen(str1) + 1);
	strcpy(str3, str1);

	// 문자열의 내용이 동일행도 Heap 의 주소와 string pool의 주소는 서로 다르다
	printf("str1==str3:%s \n", str1 == str3 ? "같다" : "다르다"); // 다르다

	// 문자열의 내용비교는 strcmp()를 이용해야 한다
	printf("strcmp(str1,str3):%s \n", strcmp(str1, str3) == 0 ? "같다" : "다르다"); //같다

	// Heap 메모리에는 동일한 문자열을 생성하면 중복되어 생성된다
	char* str4 = (char*)malloc(strlen(str3) + 1);
	strcpy(str4, str3);

	// Heap 메모리에 저장된 내용이 동일한 문자열이라도 주소는 서로 다르다
	printf("str3==str4:%s \n", str3 == str4 ? "같다" : "다르다"); // 다르다

	// 문자열의 내용이 동일하면 strcmp()로 비교했을 때 항상 동일하다는 결과(0)가 나온다
	printf("strcmp(str3,str4):%s \n", strcmp(str3, str4) == 0 ? "같다" : "다르다"); //같다

	// 문자열 포인터, 문자배열, 동적메모리 문자열의 비교
	// 메모리의 3가지 영역에 저장된 "Hello" 문자열은 그 주소가 모두 다르다
	char chArr[] = "Hello";
	char* chPtr = "Hello";
	char* chMalloc = (char*)malloc(6);
	strcpy(chMalloc, "Hello");

	printf("chArr==chPtr:%d \n", chArr == chPtr); // 0(false)
	printf("chArr==chMalloc:%d \n", chArr == chMalloc); // 0(false)
	printf("chPtr==chMalloc:%d \n", chPtr == chMalloc); // 0(false)

	// Stack, Literal Pool, Heap 영역에 "Hello" 문자열이 저장되어 있으므로
	// 문자열의 내용은 동일하지만 그 주소를 비교하면 모두 다르다
	printf("chArr:%p, chPtr:%p, chMalloc:%p \n", chArr, chPtr, chMalloc);

	// 각 메모리 영역에 저장된 "Hello" 문자열은 strcmp()로 비교하면 모두 동일한 결과가 나온다
	printf("strcmp(chArr,chPtr):%d \n", strcmp(chArr, chPtr)); //0(동일)
	printf("strcmp(chArr,chMalloc):%d \n", strcmp(chArr, chMalloc));//0(동일)
}


문자열 포인터/문자배열의 비교 예

#include <stdio.h>
#include <string.h> // strcmp()

#pragma warning(disable:4996)

char* getStr();

int main() {

	// 문자열 포인터를 이용하여 선언된 문자열은 Literal Pool에 저장됨
	char* str1 = "Hello";
	char* str2 = "Hello";

	printf("str1의 주소=%p \n", str1); // 문자열 2개의 주소가 완전히 동일함
	printf("str2의 주소=%p \n", str2);

	printf("str1==str2:%d \n", str1 == str2); // 1(true), 주소가 일치함
	printf("strcmp(str1,str2):%d \n", strcmp(str1,str2)); // 0(내용이 동일함)

	// 문자열 배열을 이용하여 선언된 문자열은 Stack이나 Data Segment에 저장된다
	char arr1[] = "Hello";
	char arr2[] = "Hello";

	printf("arr1의 주소=%p \n", arr1); // 2개의 배열의 주소는 서로 다름
	printf("arr2의 주소=%p \n", arr2);

	printf("arr1==arr2:%d \n", arr1 == arr2); // 0(false), 주소가 다름
	printf("strcmp(arr1,arr2):%d \n", strcmp(arr1, arr2)); // 0(내용이 동일함)

	char* s = getStr();

	printf("s의 주소=%p \n", s); // 문자열 2개의 주소가 완전히 동일함
	printf("str1의 주소=%p \n", str1);

	return 0;
}

char* getStr() {
	return "Hello"; // 함수 내에서 선언한 문자열 포인터이므로 Literal Pool에 저장됨
}


char* str = (char*) malloc(6);

str = "Hello"; // 위에서 할당된 메모리 공간에 문자열이 저장되는 것이 아니다

// 포인터을 이용하여 문자열을 생성한 경우에는 Literal Pool에 문자열이 생성되므로 위의 경우에는 동적으로 할당된 공간을 활요하고 있는 것이 아니다