// 이 글은 C 소스입니다. //은 주석입니다.
// 실제로 붉은 글씨가 진짜 소스입니다.
// VC++로 읽어서 Workspace 창을 죽이고 보는게 한눈에 들어옵니다. // 본 문서에 "디버깅 한다"라는 말은 버그를 잡는게 아니라 디버깅 툴을 이용해서 변수값의 변화를 알아본다는 뜻입니다.
// 문제) /* void main(void) { char szStr1[128], szStr2[128];
scanf("%s", szStr1); // 1:se119 2:se119 <- 그대로 입력(중간에 공백문자, 스페이스 또는 탭) // 공백문자 때문에 다음 scanf가 자동로 작동함(너무 잘 아는 사항^^) // 자동 작동이 아니였으면 이 문서 자체가 있을 수 없습니다...
// 이곳에 한줄 코딩으로...
scanf("%s", szStr2); printf("%s %s", szStr1, szStr2); } */ /* 출력 결과가 1:se119 2:sex19 가 되게 하시오.
2번째 se119의 1이 x가 되게... 조건 : szStr1, szStr2를 사용하면 안됨. 틀리없이 szStr2[4] = "x"; 라고 생각하는 사람이 있을텐데... printf함수 바로 윗줄에 쓰면 되겠지만 그런 문제 낼리가요^^
*필독* 문제형태로 문제제기를 한것 뿐이지 알고리즘 문제도, C언어 문법 문제도 아니기 때문에 고민해봐야 도움될께 없으니까(고민해봐도 상관은 없지만 아까운 시간을 뺏고 싶지 않다는게 본인 생각임, 이거 읽는대만도 적지 않은 시간이 걸림) 그냥 아래 내용을 소설 읽듯이 읽기바랍니다. 그냥 저사람은(나^^) C프로그래밍에 대한 생각의 흐름을 저런식으로 하는구나...라고 느끼고 취할건 취하고 버릴건 버렸으면 하는 맘에 작성한 것입니다. 물론 아래와 같이 순차적으로 해결의 실마리를 풀어나갔다면 제가 천재지요... 이래저래 왔다갔다 하다가 나름대로 정리 한것이라는걸 밝혀두는 바입니다. */
#include <stdio.h> #include <conio.h>
void main(void) { char szStr1[128], szStr2[128];
scanf("%s", szStr1);
// 일단 scanf에 대해 알아봐야겠군... // => scanf() : stdin 스트림으로부터의 입력을 탐색하고 형식화한다. // 그렇담... 스트림은 버퍼 즉, 메모리 공간이니까 stdin은 포인터일테고 // 메모리 포인터만 있으면 값 바꿔치기 하는거 쯤이야^^ // 그럼 stdin은 단순 char형 배열 일수도 있고 구조체 포인터 일수도 있겠지만 // 단순 char형 배열이라면 너무 단순하지... 십중 팔구 구조체 포인터에 한표! 올인!(요즘 인터넷 용어라네요...) // stdin이 뭔지 찾아봐야겠군... // => #define stdin (&_iob[0]) => 키보드 입력용 버퍼 // => #define stdout (&_iob[1]) => 모니터 출력용 버퍼 // => #define stderr (&_iob[2]) => 에러 출력 버퍼(모니터 출력용 버퍼랑 비슷) - 자세한 사항 모름 // 역시나 배열의 첫번째 방 주소군... 설마 char형 배열은 아니겠지^^ // _iob란 놈이 무슨 형의 포인터일까? 십중 팔구 구조체형 포인터 겠지... // => _CRTIMP extern FILE _iob[]; // 역시나 FILE형 배열 포인터로구만... // 다른건 몰라도 FILE형이 구조체란건 알고 있었지ㅋㅋ 결국 FILE형 구조체 안에 // char형 포인터가 있을테고 거기에 scanf()가 문자를 주렁주렁 단단 소리군... // 첨엔 scanf()가 malloc()해서 문자를 받아 들인다고 생각했는데 디버깅 결과 아닌거 같네요... // scanf()들어가기 전에 이미 stdin의 char형 포인터에 쓰레기 값이라고 보기 힘든 값이 들어있고 // scanf()실행 후에도 그 값이 변하지 않는거 보니 scnaf()내부적으로 malloc()하는게 아닌게 분명한거 같네... // 미리 128이나 256바이트 정도 할당 해놓고 쓴다고 보는게 맞다는데 한표! 올인! // (확인결과 처리계에 따라 8192 or 4096 바이트가 할당되었었습니다.) /* struct _iobuf { char *_ptr; int _cnt; char *_base; => 문자열 저장소 int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; };
typedef struct _iobuf FILE; */ // 디버깅 결과 _base가 문자를 달고 있네^^(_ptr 도 재밌는 동작을 하는 포인터^^ 궁금한 사람은 디버깅해보기 바람니다) // 가만... fscanf(stdin, "%s", szStr); 이거나 scanf("%s", szStr); 이게 같다는게 생각나네!! // => _CRTIMP int __cdecl fscanf(FILE *, const char *, ...); // 헐 역시나 stdin은 FILE형 포인터 맞구만^^ // 그럼 답이 나오지^^ // _base배열의 값을 바꿔치기 하면 키보드로 부터 들어온 값을 바꿔치기 가능하겠네...
stdin->_base[12] = "x";
// stdin은 _iob의 매크로니까 // _iob->_base[12] = "x"; 이것도 가능하지... // 매크로 그대로 쓰자면 // (&_iob[0])->_base[12] = "x"; 이것도 가능하고... // 이쯤 되면 포인터 표현으로 바꾸고 싶어하는게 C프로그래머의 좋지 않은 습관... // *(((*_iob)._base)+12) = "x"; // 그냥 C언어의 포인터 이해를 위함이지 절대 이런식으로 코딩하면 안된다고 생각합니다... // 왜냐? 배열 첨자 표현을 써도 결국 컴파일러는 포인터 표현으로 바꿔서 컴파일을 하는걸 보면 // 굳이 이해하고 쓰기 편하라고 만들어놓은 // syntax sugar(신택스 슈거, 구문 설탕?? 쓰기에 편하다는 거죠^^)인 // 배열 첨자 표현을 안쓴다는건 연구하는 학자가 할 짖이지(학자는 포인터표현을 써야한다는 이론적 근거도 없음) // 생산성이 더 중요한 엔지니어가 할 짖은 아니라는 거죠...
scanf("%s", szStr2); printf("%s %s", szStr1, szStr2);
// 결론은... "C프로그래머는 전지전능하다"는 이념 하에 C언어가 설계되었다고 합니다. // ANSI-C의 규격은 아니지만 ANSI가 작성한 자료중 // Rationale(이론적 근거)라는 문서에 "Keep the spirit of C"(C의 정신을 유지하라)라는게 있는데 // 5가지의 "spirit of C" 중 1항과 2항은 이렇습니다... // 1. Trust the programmer.(프로그래머를 믿어라) // 2. Don"t prevent the programmer from doing what needs to be done. // (프로그래머가 무엇인가 필요한 조치를 취하려 할 때 그것을 방해하지 말아라.) }
// 사실 아래같은 소스를 만들다가 위와 같은 문서를 작성하게 된건데... // 뭐 디게 많은 지식을 습득한거 같지만 결국 scanf() 내부까지 손을 댈수 없으면 // 목적한 코드를 만들어 낼수 없다는 결론을 얻었습니다.
/* void main(void) { int ch, i;
ch = getch(); // "5" 입력
putc(ch, stdin); // 표준 입력 스트림에 "5"출력
scanf("%d", &i); // "4"입력->엔터
// 결국 i는 int형 45이길 바라는 어처구니 없는 소스... } */
// 어처구니 없는 이유 2가지 // 1. putc는 stdin의 _base에 문자를 넣지도 못함.(EOF 리턴 : 스트림에 출력하지 못했다는 말) // 2. 만약 성공했다 하더라도 scanf()가 친절하게 stdin의 _base에 문자가 들어있다면 그 뒤부터 문자를 채우지도 않는다. // _base 포인터는 변하지 않고 항상 첫번째 방만 가리키고 있고 항상 처음부터 값을 넣는다. // 그래서 강제로 _base[0]에 값을 넣고 _base++해서 scanf()가 stdin을 이용하도록 해봤지만 // scanf()는 증가된 _base 부터 문자를 채우긴 하는데 결국 증가된 _base 부터 값을 읽어서 // scanf()의 서식 지정자에 맞게 변수에 넣어주더군요... 제기랄... // scanf()가 값을 읽는 단계 바로 전에 _base--; 라는 코드를 넣을수 있다면 얼마나 좋을까...
/*
공개된 ANSI-C 함수 소스에 있어서 봤는데... (차후에 자료실에 올리겠습니다.) int scanf(const char *fmt, ...) { int r; va_list a=0; va_start(a, fmt); r = _doscan(stdin, fmt, a); va_end(a); return r; } */ // typedef char * va_list; 이것만 알아냈는데... 다른건 VC++에는 없는 함수더군요... // 결국 _doscan()이란 놈안에 들어가서 _base--를 박아야 한다는... 골 아프다...
// 결국 putc(ch, stdin);은 있으나 없으나 매한가지인 명령 라인...
// 마지막 결론... // 하려고 했던 작업 포기... 그냥 쉽게 가야겠습니다... // 입력 에러 무시!! 입력 인터페이스 편리성 무시!! // 이런 코드가 있어야겠죠... // printf("입력은 정확하게 하시고... ESC, m 키는 누르기만 하면 되고 숫자는 입력후 엔터를 치세요!! // 숫자 입력시에는 최초 숫자 입력키는 무시됩니다...ㅜㅜ");
|