본문 바로가기

무언가 만들기 위한 지식/C,C++,Embedded C

[C,CPP,embedded C] String Constant(문자열 상수)


필자가 처음 공부좀 한답시고 포인터와 문자열에 관한 책을 봤을때,
문자열에 대한 생각은,
[문자열은 또하나의 배열이다]
라는 생각만 갖고 있었다.
하지만 이번에 다시 공부를 하게 되면서 새로운 것들을 알게 되었다. 아니 좀더 컴퓨터란 녀석에 대해 분석을 할 수 있었다.

일단 테스팅 예제를 보면서 문자열(String)이 무엇인지에 대해 알아보자.
아래 테스트는 배열을 통한 char [] 문자열 저장과, char * 포인터를 이용한 차이점을 확실히 알 수 있고,
RODATA영역, RWDATA영역, STACK영역에 대해 개념을 잡을 수 있었다.

[작성환경 : Window XP  작성툴 : Visual Studio 6.0  컴파일러 : Visual Studio 6.0  사용언어 : C]



코드상에는 대부분이 출력 코드이므로 결과 값을 가지고 설명을 할 것이다.
설정된 변수에는 main함수 외부의 bssInt, bssInt2, dataInt 3개와 main 내부의 str, stackInt, strRODATA 3가지가 존재한다.
먼저 이들이 저장되는 영역은 4가지로 구분된다.
첫번째로 Stack영역
- 일단 main내부의 변수들은 모두 포함된다.(Heap 할당 제외) 그중 str과 stackInt 변수가 있다.
   단 strRODATA는 예외.(상수취급이므로 RO-DATA 즉 TEXT 영역에 속함)
두번째로 Data 영역
- Data영역은 쓰고 읽히는 Data영역 RW-DATA와 RO-DATA로 나뉜다. 여기서 RO-DATA는 Text영역으로 취급되면 RW-DATA는 Data영역으로 취급된다. RW-DATA는 주로 램상에 로딩되어 사용되고, RO-DATA는 경우에 따라 램상에 로딩되기도 하며 롬상에서 처리되기도 한다.
세번째로 BSS 영역
- BSS(Block Started by Symbol) 영역이다. 이곳에는 초기화되지 않은 영역이다. 초기화되지 않은 영역은 불필요하게 컴파일 되면서 초기화 되어 영역을 잡아 놓지 않고, 단지 이렇게 선언 할 것이라는 정보만을 저장해 놓는다. 즉 초기화되지 않았기 때문에 영역을 실제 메모리에 확보하는 불필요한 동작을 하지 않는다. 무조건 초기화를 안하였다고 BSS로 되는 건 아니다. 보통 함수 밖에서 설정한 "초기화 하지 않은 변수"들이 BSS영역에 위치한다. 
네번째는 Text 영역 이다.
- Text영역에는 RO-DATA(Read Only Data)와 code가 존재하게 된다. code란 실제로 우리가 작성한 코드가 저장된 위치를 말한다. (예를 들면 main)

결과값의 첫번째 strRODATA는 420000번대의 주소를 갖는다. 이는 주소값들을 비교해보면 "String"문자열의 주소와 비슷한 위치를 갖으며 dataInt의 주소값과도 가까운 주소를 갖는다.
이는 구조가

위와 같은 그림에 의존한다.
main을 출력해보면 401005의 주소를 같는다. main-즉 함수명은 함수의 위치를 말하므로 Text Field의 위치를 추측이 가능하다. 또한 "String"이라는 문자열의 주소를 출력해보면 42002c가 나온다. 이는 RO-DATAstrRODATA와 같이 수정이 불가능한 상수값을 갖는 부분인 Text내부 RO-DATA영역에 존재한다. 이 둘의 주소값이 근접한 사실을 확인할 수 있다.
배열 str stackInt의 경우 모두 main내부의 Stack 공간에 할당된다. 이 둘의 주소를 비교해보면 12ff74와 12ff70으로 선언된 시접에 있어서 바로 인접하여 있다.
다음으로 bssInt를 출력하면 0으로 초기화된 사실과, 주소값이 4237a8로 "String"인 문자열과 dataInt의 주소와 근접한 사실을 알 수 있다. 이로써 BSS영역, Data영역, Text영역이 어느정도 비슷한 위치에 존재함을 확인가능하다.
여기서 dataInt변수는 data영역에 존재한다. 지켜볼 점은 2,3,4 Line에서 2,4는 가운데 다른 변수가 존재하지만 3 Line의 dataInt data영역에 존재하고 bssInt2bssIntbss영역에 존재하기 때문에 이 둘 값은 약간의 차이를 보인다. (bssInt2bssInt2는 바로 근접한 위치이다.)
테스트 코드상 또다른 흥미로운 점은 "String"이라는 문자열상수에 대해 +1이나 *연산이 가능하다는 것이다. 이는 문자열 상수자체가 마치 배열이름 처럼 사용된다는 점을 보여준다. 즉 해당 문자열상수의 첫주소를 반환한다. 이는 배열처럼 []연산자를 통한 Indexing 접근도 가능하며 주소연산을 통해 내부 요소로 접근이 가능하다.

위표와 테스트 결과를 자세히 보면 의심할 여지가 생긴다. 그부분은 text영역이 (그림상에서) 분명 낮은 메모리 주소에 위치하는데도 불구하고 Stack영역의 주소값보다 더 높은 주소값을 갖고 있는다.
이점은 사실 OS상에서 Stack과 Heap의 경우 재배치 하기 때문에
(여러 프로그램이 존재한다면 각 프로그램의 Stack영역과 Heap 영역을 각각 모아서 배치한다.)
나타나는 현상이다. (Test를 Window 상에서 하였다.)


다음은 두개의 문자열이 하나는 Stack에 하나는 RO-DATA(Text영역)에 할당된 코드이다.
[작성환경 : Window XP  작성툴 : Visual Studio 6.0  컴파일러 : Visual Studio 6.0  사용언어 : C]

위 코드상에서 str1은 배열로서 Stack에 할당되었고, str2는 문자열상수로서 Text영역에 RO-DATA영역에 할당되었다.
그렇기 때문에 7 Line에서처럼 str1은 원소의 Write 즉 수정이 가능하나
8 Line에서처럼 문자열상수로의 Write 즉 수정은 불가능하다.
강제로 수정하려한다면 다음과 같은 런타임 오류를 보게 된다.
아래 오류에서 0x0040b887이라나 바로 8 Line의 위치, 즉 Text영역의 값이 존재하는 곳이고, 0x00420028은 문자열 상수가 존재하는 Text영역내의 RO-DATA영역상의 문자열 상수 str2의 위치를 지칭하는 것이다.


<문자열 상수 영역에 대한 잘못된 접근>


<정상적으로 실행되었을 경우>

하지만 한가지 고려해야할 사항은 위와 같은 결과가 컴퓨터 방식에 따라 차이가 난다는 것이다.
윈도우 OS상에서 실행할 시는 100%위와 같은 오류가 발생한다.
그러나 임베디드 환경에서는 에러가 발생하지 않는다.
그 경우
1. ROM상에서 프로그램을 구동하게 되면 그대로 RO-DATA영역에 있기 때문에 문장이 실행되지 않는다.
2. RAM에 로딩하여 프로그램을 구동하게 되는 경우 RO-DATA영역이 RAM상으로 전체가 복사된후 실행되기 때문에 자연스럽게 실행이 잘된다.
그렇기 때문에 무조건 접근이 가능하다고 하면 잘못된 말이고 부팅 환경도 고려해야 한다.


마지막으로 자주 사용되는 포인터배열에서 String사용예를 보자.
[작성환경 : Window XP  작성툴 : Visual Studio 6.0  컴파일러 : Visual Studio 6.0  사용언어 : C]

다음은 2차원배열을 이용한 test1 함수와 배열포인터를 이용한 test2 함수이다.
둘다 잘돌아간다. 그런데 이 부분을 최적화라는 입장에서 확인해 봐야 한다.
test1 함수의 경우는 각각 4칸, 6칸, 4칸, 즉 통합 14byte가 잘못된 할당으로 낭비되었다.
그에 비해 test2는 이와 반대로 낭비된 메모리공간은 전혀 없다.
이부분에서 분명 확실히 메모리 공간상 효율면에서는 test2 함수가 월등히 뛰어나다.
하지만 다른 측면에서 분석하면 결과는 다르다.
test1의 경우 stack영역에 저장된다. 즉 RAM상에 항상 로드되어 사용된다.
이와 반대로 test2의 경우는 문자열상수를 사용한다. 이 문자열 상수는 RO-DATA영역이라 환경에 따라 ROM상에서 실행될 경우가 있다. 그럴 경우 RAMROM보다 Read속도가 빠르기 떄문에 속도면에서 차이가 난다.
즉, 속도면에서는 test1이 더 빠르다.
(물론 RAM로딩의 환경일경우에는 이는 상관없다.)

무조건 test2가 좋은 것은 아니다.
 환경에 따라 속도와 메모리 공간을 잘 고려하여 어떤 방식을 사용할지 결정하여야 한다.


<출력결과값, 420030을 통해 TEXT영역에 상수로서 저장된 모습이 확인이 가능하다.>