[C] C스타일 문자열의 차이 (char 포인터, char 배열) by 메모리 영역

Date:     Updated:

카테고리:

태그:

🚀 메모리 구조와 문자열 리터럴

“Hello World” 같은 문자열 리터럴은 메모리의 TEXT SEGMENT 영역에 저장되며 이 영역은 Read-Only 영역이다.

image

메모리는 위와 같은 영역들로 나눌 수 있다.

  • 메모리 구조
    • TEXT SEGMENT
      • 프로그램 파일의 내용이 저장된다. 즉, .exe 내용인 기계어. 그리고 3, 'a', "Hello" 같은 리터럴이 저장된다.
      • 개발자가 절대 수정할 수 없는 Read-Only 영역이다.
    • DATA SEGMENT
      • 프로그램이 실행되자마자 전체가 0 으로 자동 초기화되고 프로그램 시작부터 종료때까지 유지되는 전역 변수(global), 정적 변수(static)가 저장되는 공간이다.
    • STACK
      • 우리가 main 함수를 비롯한 함수들 내에서 흔하게 사용하는 변수들은 모두 이 스택 메모리에 저장된다.
      • 스택은 함수가 작업을 수행하는데 사용하는 메모리 공간이다.
      • 모든 “지역 변수”는 이 스택 메모리에 저장된다. 지역 변수의 scope 가 끝나면 해제된다.
    • HEAP
      • 동적 할당 받는 메모리.
      • 자동으로 해제 되지 않기 때문에 힙 메모리를 사용할 때는 메모리 누수가 발생하지 않도록 관리에 신경써야 한다.
        • 개발자가 free(C), delete(C++)을 통해 직접 해제해주어야 한다.

리터럴은 프로그램 코드(기계어)가 저장되는 메모리 영역인 TEXT SEGMENT 영역에 저장된다.(전역 변수, 정적 변수가 저장되는 DATA SEGMENT 영역에 저장된다는 이야기도 있는데 컴파일러마다 다르다고 한다.) 이 TEXT SEGMENT 영역은 프로그램 코드가 저장되는 공간이기 때문에 절대 수정할 수 없는 Read-Only 영역이다. 즉, 리터럴 그 자체는 수정할 수가 없다는 이야기이다.

이 지식을 알고 있다면 char *char []의 차이를 알 수 있게 된다.


🚀 char 포인터로 문자열 표현하기

🔥 char*

image

	char* str1 = "Hello";
	printf("%s\n", str1);

	str1[1] = 'A'; // ❌ 📢"런타임"에러 발생!
	printf("%s\n", str1);

문자열 리터럴을 가리키는 char 포인터Read-Only 한 TEXT SEGMENT 에 위치한 문자열 리터럴을 가리키는 것이다.

따라서 char 포인터로 문자열 리터럴을 가리키는 경우에는 문자열 원소를 수정할 수가 없다. 컴파일 에러는 발생하지 않았지만 문자열 리터럴의 내용을 간접참조로 수정하려는 부분에서 런타임 에러가 발생한 것을 확인할 수 있었다.(str1[1]은 곧 *(str1 + 1) 과 같다.) char 포인터인 str1이 가리키는 "Hello"는 TEXT SEGMENT 에 저장되어 있어 절대 수정할 수 없는 영역이기 때문이다.


🔥 const char*

image

	const char* str1 = "Hello";
	printf("%s\n", str1);

	str1[1] = 'A'; // ❌ 📢"컴파일"에러 발생!
	printf("%s\n", str1);

char* 보단 const char* 사용을 권장한다.

const 포인터는 간접 참조로 수정하려는 행위를 막아준다. 그러므로 char 포인터로 문자열을 표현할 땐 `const char *`를 사용하는 것을 권장한다. 어차피 “Hello”는 절대 수정할 수 없기 때문에 char *로 참조 중이더라도 값을 수정할 수가 없다.(실행해봐야지만 에러가 난다는 것을 알 수 있는 런타임 에러가 발생하므로 더 치명적이다.) const char *로 선언하면, 리터럴 내용을 수정하려는 시도를 하자마자 컴파일 에러를 발생시키므로 실행 전부터 바로 막아주고 개발자에게 이게 문법적 오류라는 것을 알려줄 수 있기 때문이다.


🚀 char 배열로 문자열 표현하기

🔥 char []

image

	char str1[] = "Hello";
	printf("%s\n", str1);

	str1[0] = 'A';
	printf("%s\n", str1);

char [] 배열은 TEXT SEGMENT 에 있는 문자열 리터럴 원본을 복사하여 STACK 메모리에 사본으로 만든 "char 문자의 배열"을 사용한다. 따라서 수정이 가능하다.

즉, TEXT SEGMENT 에 존재하는 “Hello” 가 원본이고, 이 원본을 복사하여 STACK 스택 메모리에 가져온 사본을char [] 배열인 str1에서 배열로서 관리하게 된다. 즉, 전체 메모리에 “Hello”가 2 개 있는 상태인 것이다.

  • in TEXT SEGMENT 👉 “Hello” (원본)
  • in STACK 👉 ‘H’ ‘e’ ‘l’ ‘l’ ‘o’ (사본)

따라서 char* 포인터로 문자열 리터럴 원본(in TEXT SEGMENT)을 직접 가리키던 것과는 달리 char [] 문자열 배열은 이 원본 “Hello”을 사본으로 복사하여 스택 메모리에서 관리를 하기 때문에 Read-Only 같은 규칙이 없어 자유롭게 수정할 수 있게 된다. 즉, 사본을 수정하는 것이다.

문자열 리터럴이 아주아주 길고 크기가 큰데 수정할 일은 없다면 const char* 로서 직접 원본 리터럴을 가리키도록 하는게 메모리 효율성 면에서 나을 것 같다.



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

C 카테고리 내 다른 글 보러가기

댓글 남기기