C 함수의 호출 원리를 이용한 sprintf() 의 약식 구현
글쓴이: onezero3 / 작성시간: 목, 2003/09/04 - 9:40오전
C 함수의 호출 원리를 이용한 sprintf() 의 약식 구현 입니다. va_start() 나 va_end() 함수를 사용하여 구현할 수도 있지만, 이것도 stack의 동작을 숨기고 있어서 좀더 low level 로 구현해 보았습니다.
date 함수와 같이 자신만의 형식으로 포맷을 재구성할 때 사용할 수 있습니다.
/*+-------------------------------------------------------------------------+ | FILE: sprintf.c | | Version: 0.1 | | | | Copyright (c) 2003 Chun Joon Sung | | Email: chunjoonsung@hanmail.net | +-------------------------------------------------------------------------+*/ char *itoa( char *a, int i) { int sign=0; int temp=0; char buf[16]; char *ptr; ptr = buf; /* zero then return */ if( i ) { /* make string in reverse form */ if( i < 0 ) { i = ~i + 1; sign++; } while( i ) { *ptr++ = (i % 10) + '0'; i = i / 10; } if( sign ) *ptr++ = '-'; *ptr = '\0'; /* copy reverse order */ for( i=0; i < strlen(buf); i++ ) *a++ = buf[strlen(buf)-i-1]; } else *a++ = '0'; return a; } char *xtoa( char *a, unsigned int x, int opt) { int i; int sign=0; int temp=0; char buf[16]; char *ptr; ptr = buf; /* zero then return */ if( x ) { /* make string in reverse form */ while( x ) { *ptr++ = (x&0x0f)<10 ? (x&0x0f)+'0' : (x&0x0f)-10+opt; x>>= 4; } *ptr = '\0'; /* copy reverse order */ for( i=0; i < strlen(buf); i++ ) *a++ = buf[strlen(buf)-i-1]; } else *a++ = '0'; return a; } long _sprintf(buf, format, arg) char *buf; char *format; long arg; { int cont_flag; int value; int quit; char *start=buf; long *argp=(long *)&arg; char *p; while( *format ) { if( *format != '%' ) /* 일반적인 문자 */ { *buf++ = *format++; continue; } format++; /* skip '%' */ if( *format == '%' ) /* '%' 문자가 연속 두번 있는 경우 */ { *buf++ = *format++; continue; } switch( *format ) { case 'c' : *buf++ = *(char *)argp++; break; case 'd' : buf = itoa(buf,*(int *)argp++); break; case 'x' : buf = xtoa(buf,*(unsigned int *)argp++,'a'); break; case 'X' : buf = xtoa(buf,*(unsigned int *)argp++,'A'); break; case 's' : p=*(char **)argp++; while(*p) *buf++ = *p++; break; default : *buf++ = *format; /* % 뒤에 엉뚱한 문자인 경우 */ break; } format++; } *buf = '\0'; return(buf-start); } main() { char buf[80]; _sprintf( buf, "%d, %x, %X, %s\n", 1234, 0xabcd, 0xabcd, "abcd"); puts(buf); }
Forums:
stdarg 계열 함수가 지원하는 것이 architecture 마다 다른
stdarg 계열 함수가 지원하는 것이 architecture 마다 다른 것을 감싸기 위해 있는 것이고, i386 계열에서는 이렇게 동작하는 것이 맞겠지요.
모든 arch에서 argument가 stack 으로만 전달되는 것은 아니니까 사용에 제약은 있겠습니다.
또한가지.. 이것은 C에서만 동작하겠습니다. argument 를 통해 name mangling이 일어나는 C++에서는 호출하는 쪽에서볼때, 다른 함수를 호출하게 되고, 결과적으로 link가 안됩니다. C 에서도 만약 argument가 다른 것을 확인한다면, compile 부터 안될 것입니다.
만약, 위 소스가 컴파일/링크가 제대로 되는 환경이라면, 여전히 위 소스는 stack에 전달되는 것을 이해하기에는 좋은 예제입니다.
---
http://coolengineer.com
이 함수의 원래의 용도는..
이 함수는 원래 embedded target 에서 커다란 라이브러리를 올릴 수 없어서 꼭필요한 함수인 sprintf() 만 구현 하였던 것 입니다.
제가 사용해본 대부분의 32비트 프로세서에서는 잘 동작 합니다.
C 함수의 Calling Convension을 잘 따르는 컴파일러라면, 그리고 어셈블리로 된 Source 와 함께 링크가능한 컴파일러라면 문제 없이 동작 합니다.
Re: 이 함수의 원래의 용도는..
혹시 모르는 분들이 계실까 해서....
newlib에 보면 siprintf라는 게 있습니다...
http://sources.redhat.com/newlib/
예전에 프로젝트 할 때 잘 써먹었었죠... :)
Consider the ravens: for they neither sow nor reap; which neither have storehouse nor barn; and God feedeth them: how much more are ye better than the fowls?
Luke 12:24
이런식으로 쓸만한 또다른 이유가 있죠.
예전에 8051가지고 작업하는데...
(물론 8051에는 va_list도 있고, sprintf도, vsprintf도 있습니다만)
프로그램 메모리(롬이 모자랐는지 램이 모자랐는지는 잘 모르겠군요.)가
모자라는 상황에... -.-;
이리저리 고민하다가 이유없이 플로팅 포인트 라이브러리가 포함되어 있는걸
보고 printf 계열 함수를 몽땅 제거했습니다. 물론 printf 계열 함수를 다
새로 만들어줘야 했죠. 속도에는 문제가 좀 있었지만 그럭저럭 제한된 공간안에
적당히 구겨넣을 수는 있었습니다.
좋은 환경에서는 저런짓 할 필요가 거의 없다고 보여지네요. ^^;
--------------------------------------
재미없는 일은 하지 말자는 인간 쓰레기.
-.-;
댓글 달기