티스토리 뷰

분석/C

fgets와 입력버퍼

Turtle1000 2014. 2. 5. 14:53

평소에 궁금한 것들은 혼자서 분석해보고 '아... 내부적으로 이렇게 되는구나...' 하고 넘어갔는데 이제부터는 분석내용을 블로그에 쓰기로 결정했습니다 ㅎㅎ.

일단 이번에 분석할 코드는 아래와 같습니다.

코드 내용은 매우 쉽죠 ? 윤성우 님의 열혈 C 프로그래밍에 있는 예제입니다 ㅋㅋ...

제가 궁금했던 것은, 

"fgets를 수행하면 입력버퍼에 모든 내용이 들어가게 되는데, 어째서 그 다음 fgets에 영향을 미치는 것인가." 입니다.

결론부터 말씀드리면, fgets 함수의 맨 뒤에 입력하는 stdin이라는 곳에서 값을 받아오기 때문입니다.

(분석을 완료하고나서 곰곰히 책을 다시보니 윤성우 님께서 얼마나 자세히 설명을 해주시려던 것인지 느껴지더군요...)

디버깅을 할 줄 모르셔도 따라올 수 있을만큼 쉽게 설명하겠습니다. 리눅스를 많이 쓰다보니 GUI 디버거가 불편해져서 gdb로 디버깅을 진행하겠습니다. 먼저 fgets가 내부적으로 어떻게 변환되는지 봐야합니다.

fgets(perID, sizeof(perID), stdin) 이라는 형태가 위와 같이 나타나는군요.

'ds:0x8049764=stdin , 0x7=sizeof(perID), lea eax, [ebp-24]; push eax=perID의 주소를 넣어준 것'입니다.

자료구조를 공부하셨거나 스택에 대해서 알고 계신다면 왜 이렇게 데이터가 입력되는지 아시겠지만 모르시더라도 그냥 그렇구나... 하고 보셔도 됩니다.

아무튼 중요한 것은 ds:0x8049764가 stdin이라는 것입니다. 한 번 따라가보겠습니다.

일단 네 자리의 1234를 입력했습니다. 그럼 다섯 개가 입력되겠죠 ?( 1234 + 엔터[0x0a] )

stdin을 쫓아가봤더니, 0부터 5전 까지 다섯 개가 입력되었다는 것을 볼 수 있습니다.( 0x40~ 부분의 끝부분만 보세요.)

그리고 두 번째 빨간네모칸 안의 순서가 5 5 0 으로 된 것을 볼 수 있습니다.


첫 번째 5는 fgets로 0~5전까지 받았다는 것을 의미합니다.

두 번째 5는 stdin이라는 임시버퍼가 0~5전까지의 문자를 입력받았다는 것입니다.( 5개의 문자 )

세 번째 0은 stdin의 시작주소입니다.


그렇다면, 123456을 입력했을 때는 어떻게 나타날까요 ?

세 개의 숫자가 6 7 0 으로 다르게 나타납니다.


첫 번째 6은 0~6전(6개)까지 밖에 fgets가 받아들이지 못한다는 것을 의미합니다.

두 번째 7은 stdin이라는 임시버퍼가 갖고 있는 값이 0~7전(7개)까지 받아들인 것을 의미합니다.( 123456 + 엔터 )

세 번째 0은 stdin의 시작주소입니다.


위와 같은 상황이 발생했을 때 디버깅을 계속 수행해보면,

첫 번째 빨간네모칸은 전의 6 7 0 이고 따로 설명하지 않겠습니다. 중요한 것은 하얀 글씨로 "fgets 함수" 라고 써놓은 부분입니다. 값을 입력받지 않고 바로 넘어가는 것을 볼 수 있는데요.

아무튼 넘어가고 다시 stdin부분을 보면 이번에는 7 7 0 으로 되있는 것을 볼 수 있습니다.

사실 이 부분을 제대로 이해하려면 fgets 내부적으로 호출하는 fgetpos 함수와 fgets의 내부동작을 이해해야하지만... 

그 부분까지 설명하려고하면 너무 복잡하기도 하고, 저도 완벽히 분석한게 아닌지라(내공도 부족하고 귀찮기도 합니다 ㅠㅠ...) 글로만 설명드리겠습니다.

일단 fgets 함수는 문자열이 입력되면 stdin(임시버퍼)의 길이를 비교합니다.

그래서 입력받은 길이가 fgets에서 지정한 길이보다 길 경우 다시 입력을 받지 않고 그 다음 입력받는 함수로 넘겨버립니다. 그래서 다음과 같은 결과가 나오게 되는 것이죠.

왜냐하면 fgets는 책에도 써있듯이 '\n'가 나올때까지 입력을 받는데 '\n'이 바로 다음 fgets로 넘어가버리니 name 변수에 '\n'이 들어가버리고, 주민번호와 이름이 그대로 출력되는 겁니다.

글을 쓰다보니 이상하게 쓰고 있는 것 같기도한데... 이제 입력버퍼를 지워주는 함수의 주석을 지워보겠습니다.

ClearInputBuffer 함수가 호출되기 전은 위에서 봤던 결과가 똑같습니다. 호출하면,

6 7 0 에서 7 7 0 으로 바뀌었습니다. 즉, fgets가 모든 문자열을 받은 것으로 인식시켜주는 것이죠.

정말 놀라운 방법이 아닐 수 없습니다...(누가 이 방법을 발견했는지 몰라도 정말 대단한 분입니다...)

아무튼 다음 fgets로 넘어가 보면,

이제는 fgets 함수가 다시 수행되면서 값을 입력받습니다. 또한, 새로 입력받게 되면 stdin(임시버퍼)의 시작주소부터 다시 받기때문에 기존의 값을 덮어버리면서 값을 입력받습니다.

음... 다 썻는데 어떻게 마무리를 지어야할지 모르겠네요... 아무튼 fgets는 이렇게 동작합니다 !(?!?!)

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함