– 문자열은 불변(immutable) 입니다

프로그램에서 문자와 문자열은 상당히 자주 다루게 되는 자료입니다.  문자열은 연속된 문자의 자료이고, 프로그램에서 그 활용도가 매우 높습니다. 각 언어에서는 문자열을 위한 여러 함수와 라이브러리가 제공되고 있습니다. 우리가 흔히 문자열은 불변이라고 하는데, 그 내용에 대해 짧게 알아보도록 하겠습니다. 

C 언어에서 문자열을 사용하는 방법은 크게 두 가지가 있습니다. 하나는 배열을 이용하는 방법이고 또 하나는 포인터 변수를 이용하는 방법입니다.

우선 배열을 활용하는 방법을 알아보겠습니다. 아래와 같이 배열을 선언하고 문자열로 초기화를 하면

char str[] = “Hello, World”; 

Hello,
World\0

null 문자를 포함한 13개의 문자 배열이 만들어 지고 이에 각 문자가 할당됩니다. 이때는 배열의 값을 변화 할 수 있습니다. 이렇게 문자 배열로 생성되고 초기화 된 경우엔 각 배열의 값을 인덱스 연산을 통해 변경할 수 있습니다.

즉, str[4] = ‘u’;

이를 다른말로 “변수형 문자열” 이라고도 합니다.

하지만, 다음과 같이 문자열을 포인터에 대입하는 경우를 생각해 보면,

char *str2 = “Hello, World”;

이 경우는 str2[4] = ‘u’; 와 같은 연산이 처리 되지 않고 오류가 납니다.

왜냐하면 위 경우에는 “Hello, World”라는 문자열이 데이터 영역의 상수 값으로 어딘가에 이미 저장되어 있고 그 메모리의 주소를 포인터가 가리킬 뿐이기 때문입니다. 이때 상수 메모리에 저장된 문자열의 값은 변경될 수 없습니다. 이러한 경우에 문자열은 불변(immutable) 이며, “상수형 문자열”이라 합니다.

우리가 C 언어로 문자열에 관한 프로그래밍을 할때 유의할 점은 두가지인데, 하나는 이처럼 포인터로 상수형 문자열을 가리키는 경우 문자열은 변경할 수 없다는 점과 또 하나는 문자열의 끝에는 항상 ‘\0’ 문자가 있어야 한다는 것입니다. 배열이나 포인터로 메모리를 할당 받고 문자열을 직접 생성하는 경우에 (scrcat나 reverseString 같은 함수를 직접 작성 하는 등등) 맨 마지막에 ‘\0’ 문자를 넣어주어야 한다는 사실을 잊지 않아야 합니다.

자바에서는 문자열을 편리하게 사용하기 위해서 String이라는 클래스가 제공됩니다. String 클래스는 ‘+’ 연산자나 concat() 메서드를 이용하여 두 개의 문자열을 연결할 수 있습니다. 즉,

String javaStr = new String(“Java”);

String androidStr = new String(“android”);

javaStr = javaStr.concar(androidStr);

위와 같이 코딩을 하면 javaStr에 androidStr이 연결될 것이라 생각하지만, 실제로 메모리를 출력해보면 첫번째 선언된 javaStr메모리 값과 연결의 메모리 값이 다른것을 알 수 있습니다. 

이는 java의 String 클래스 내부를 보면 문자열 배열이 선언되어 있는데, final로 선언되어 있기 때문입니다. 즉, 자바에서 문자열을 처음 생성할 때 초기화된 문자열의 값은 불변(immutable) 입니다. 위의 코드의 메모리 상태를 보면, 다음과 같습니다. 

따라서 만약 자바로 프로그래밍을 할때 문자열을 생성하거나 상수 문자열을 이용하여 문자열을 변경하는 연산을 하게 되면 내부적으로는 새로운 메모리가 계속 새로 생성됩니다. 프로토콜을 xml, json 등의 포맷으로 만들어 데이터를 주고 받기위해 문자열을 마구 연결하게되면 메모리의 낭비가 심해질것입니다. 그래서 자바에서는 StringBuffer와 StringBuilder 클래스가 제공됩니다. 이 두개의 클래스 차이는 멀티 쓰레드 상에서 동기화 여부인데, 이를 모르더라도 문자열을 계속 연결할 일이 있다면 위 두 개의 클래스 중에 하나를 사용하시면 됩니다. 이 두 개의 클래스는 내부적으로 가변 배열을 가지고 있어서, 문자열이 계속 연결되거나 변경하더라도 메모리가 새로 생성되지 않고 기존의 배열의 값이 변경되고, 확장되면서 사용할 수 있습니다.  

위와 같이 문자열을 StringBuilder로 선언한 후 연결이나 변경의 연산을 수행하고 toString()메서드를 이용하면 String으로 반환 받아 사용할 수 있답니다~~~