printf 함수의 소스입니다. 무슨버그가있는지?

dopesoul의 이미지

http://library.kldp.net
의 프로젝트로 진행되는 소스입니다.
축약형 printf 인데,(이기종(arm,avr,msp430...etc) cpu 에 들어갈 예정)
그냥 찜찜해서 한번 올려봅니다. 무언가 버그가 있어보이는데
짜고나니 -_- 코드보기가 조금 싫어져서
또 여러 고수님들의 의견도 듣고싶고... 잠정적 문제점이라던가..
아직은 프로젝트가 활성화 되질 않아서 이곳이 좋아보이네요^^

#include <stdafx.h>

/*

  Small printf function

  //TODO
  Support a print with formatted;
  Support hash prefix (like 0x,o,b,...)

  prefix flag

  l : long
  # : hash prefix (not support now)

  %b : print binary number from variable
  %c : print character from variable
  %d : print signed decimal number from variable
  %u : print unsigned decimal number from variable
  %s : print string from variable pointer
  %o : print octal number from variable
  %x : print hexdecimal number form variable
  %X : print upper case hexdecimal number from variable

*/
int printf_(const char *formatstr,...)
{
	// define with maximum bytes + NULL;
	unsigned char buf[33];

	//for fast processing, using register var.
	register unsigned char *f;					// format string
	register unsigned char *bp;				// for buffer pointer skillz!
	register unsigned char fmst;				// real format string like b,c,d,s,x,X...
	register unsigned char do_long;				// get a (long) type flag.
	register unsigned char sign;				// plus or minus?
	register unsigned char div;					// divider;
	register unsigned char i;					// wide using

	register long l;
	register unsigned long u;
	//register unsigned char fcount;				// format count '%'

	//declare with a va_list struct;
	va_list ap;
	
	//define a stack bottom of args;
	va_start(ap,formatstr);

	//initialize
	//fcount = 0;
	do_long = 0;
	sign = 0;
	div = 0;
	i = 0;

	//pointer is void, so casting with uchar*
	f = (unsigned char*)formatstr;

	//get variable argument's count
	do
	{
		if(*f != '%')
		{
			putchar(*f);
		}
		else if(*f == '%')
		{
			f++;	// to meet a next character 'format string'

			if(*f == 'l')		// like %ld format string
			{
				do_long = 1;
			}

			fmst = *f;	// real format string like b,c,d,s,x,X...
			bp = buf;	// buffer pointer, and buffer

			switch(fmst)
			{
				case '%':
					putchar('%');
					break;
				case 'b':
					l = (long)va_arg(ap,int);

					if(l<0)
					{
						sign = 1;
						l = -l;
					}

					do
					{
						*bp++ = l%2 + '0';
					}while((l/=2) != 0);

					if(sign == 1)
					{
						*bp++ = '-';
					}

					for(bp--;bp>=buf;bp--)
					{
						putchar(*bp);
					}

					break;
				case 'c':
					i = va_arg(ap,unsigned char);
					putchar(i);
					break;
				case 'd':		// format str 'd' is signed decimal

					if(do_long == 1)
					{
						l = va_arg(ap,long);
					}
					else
					{
						l = (long)(va_arg(ap,int));
					}
					
					if(l<0)
					{
						sign = 1;
						l = -l;		//negative to positive
					}

					do
					{
						*bp++ = l%10 + '0';
					}while((l/=10) > 0);

					if(sign == 1)
					{
						*bp++ = '-';
					}

					for(bp--;bp>=buf;bp--)
					{
						putchar(*bp);
					}

					break;

				case 'o':
				case 'u':
				case 'x':
				case 'X':

					if(do_long == 1)
					{
						u = va_arg(ap,unsigned long);
					}
					else if(do_long == 0)
					{
						u = (unsigned long)(va_arg(ap,unsigned));
					}
					else;//Unknown error!?

					if(fmst == 'u')
					{
						div = 10;
					}
					else if(fmst == 'o')
					{
						div = 8;
					}
					
					if(fmst == 'x' || fmst == 'X')
					{
						// if HEX decimal
						do
						{
							i = u%16;
							if(i<10)
							{
								*bp++ = i+'0';
							}
							else
							{
								if(fmst == 'x')
									*bp++ = i-10 + 'a';
								else if(fmst == 'X')
									*bp++ = i-10 + 'A';
							}
						}while((u/=16) > 0);
					}
					else
					{
						// if not HEX decimal
						do
						{
							*bp++ = u%div + '0';
						}while((u/=div) > 0);
					}

					for(bp--;bp>=buf;bp--)
						putchar(*bp);
					//	putchar((int)*bp);

					break;

				case 's':
					bp = va_arg(ap,unsigned char*);

					if(!bp)
					{
						bp = (unsigned char*)"(NULL)\0";
					}

					while(*bp != '\0')
					{
						// Something dangerous?
						putchar(*bp);
						bp++;
					}

					break;

				default:
					break;
			}
		}
		else
		{
		}
	}while(*f++ != '\0');

	//rprintfNum_(10,fcount,2,' ');
}
cdpark의 이미지

당장 register 선언이 너무 많은데요? (CPU에 그렇게 레지스터가 남아돌리가..)

그리고 \ 는 지원하지 않을 계획인가요?

      if(*f != '%')
      {
         putchar(*f);
      }
      else if(*f == '%')
      { 

부분의 두 test가 중복이네요. 아예 switch로 바꾸는게 실수 방지나 추후 확장을 위해서도..

2진수나 10진수 부분에서

               if(l<0)
               {
                  sign = 1;
                  l = -l;
               } 

라는 부분이 눈에 띄네요. 음수의 표현범위가 양수보다 넓습니다! 대체로 printf류에서는 양수를 음수로 변환해서 처리하는게 보통이죠.

2진수를 찍을때는 8진수나 16진수처럼 부호없는 숫자로 취급하는게 보통 아닐까요?

그리고 이렇게 바닥부터 구현하지 마시고 원하는 라이센스(BSD나 GNU)를 정하고, 해당 라이센스로 구현된 코드를 참조(!)하는게 어떨까요?

당장 printf만 해도 %d만 달랑 쓰는 경우보다는 %3d 식으로 쓸 일이 더 많지 않을까요?

익명 사용자의 이미지

register 의 경우 컴파일러에서 지정한 범용 레지스터를 쓰기때문에
남아도는것과는 별개의 문제입니다.
stdafx.h 의 경우 쌓아둔 임의의 자료를 stack 에서 빼오는 과정이기 때문에 register 변수로 선언하는것이 두 자료구조간의 거리를 좁혀줍니다.
그래서 사용한 것이구요. 레지스터의 활용은 컴파일러의 적절한 스펙에의해
조절됩니다. 모자르거나 하는일은 발생하지 않습니다.

음수의 표현범위라는 말이 애매한데요. 표현 범위 자체는 일정합니다.
어차피 255 까지 표현가능하다면 MSB 나 LSB 의 bit 하나를 sign bit 으로
사용하기 때문에 표현범위는 일정한 것이지요. 다만 기준이 0 이냐
- 냐의 차이구요.

만약 unsigned char 형의 경우 255 까지 표현가능하고 char 의 경우
-127 ~ +127 이기때문에 표현범위는 일정하지요.
둘다 255니까요. 엄밀히말하면 소수점이 잘리므로 음수가 표현범위가
더 적지요? 254니까요. ^^

그러므로 l = -l 이라는 표현에는 이상이 없습니다.

2진수의 경우 프로그래머의 디버깅을 위해 넣어논 것입니다.
예를들어 -127 의 경우 이진수로는 1111111 은 아니지만 디버깅을
위해서 (임베디드의 경우 2진수 디버깅할 일이 진짜로 많거든요)
앞에 negative 부호를 붙여준 것입니다.

현재 glibc 의 경우 공식버전에서는 2진수 포맷 출력을 지원하지않습니다.
이유인즉, 임베디드쪽이랑은 상당한 거리가 있기 때문입니다.

그리고 주석을 보셨는지는 잘 모르겠지만^^;
%3d 의 경우는 TODO 사항입니다.
현재 이 printf 함수는 아주 초기버전입니다. 형출력만 넣어놓은상태고
형식출력은 넣지 않았습니다.

임베디드의 raw 한 환경(개발툴이라던지 그런것이 하나도 주어지지않은상태)
에서는 decimal to hex decimal, dec to bin... 등의 함수를 모두 따로
구현해주어야합니다. 아실거라고 생각합니다.

저도 GLIBC 의 stdio 관련된 함수의 소스를 찾아보았습니다.
그러나 그 실행속도가 너무 느린관계로 새로 짠겁니다. 보시면 아시겠지만
printf 함수를 구현하는데는 구조체가 필요없는데, glibc 의 경우 상호연동
성을 고려한 나머지 4kb ram 을 갖춘 임베디드 시스템에서는 돌아가기가
버거울정도의 코드를 내포하고 있더라구요.

즉, 우리의 컴퓨터는 몇 메가바이트 단위이지만, 임베디드의 경우 내부램만
따지자면 4kb 입니다. 조금 많아야 8Mbytes 죠. 조금 더달수도 있습니다^^

실제로 임베디드시스템에서는 shared library 라는 개념이 없습니다
(OS 가 올라가기 전까지)

그래서 printf 함수를 쓰기위해서 쓰는 stdio 함수들이 모두 하나의 바이너리로
들어가기때문에, 쓸데없는 기능까지 포함하게 되는 것이지요.

그래서 가볍고 임베디드 프로그래머들에 적합한 printf 함수를 구현하는것이
목적입니다.

dopesoul의 이미지

모르고 손님으로 로그인해서 썻네요...
제가 쓴글입니다.

첨언하자면 라이센스는 GNU 입니다.
kldp.net 에 프로젝트로 등록되어 있습니다.

\ 이 무엇을 의미하는지는 모르겠으나 \n 으로 보겠습니다.
당연히 지원할 예정입니다.

세벌의 이미지

dopesoul wrote:
\ 이 무엇을 의미하는지는 모르겠으나 \n 으로 보겠습니다.
당연히 지원할 예정입니다.

\n \r \t \" \' 등을 다 말씀하시는 거 아닐까요?
yui의 이미지

Anonymous wrote:
register 의 경우 컴파일러에서 지정한 범용 레지스터를 쓰기때문에
남아도는것과는 별개의 문제입니다.
stdafx.h 의 경우 쌓아둔 임의의 자료를 stack 에서 빼오는 과정이기 때문에 register 변수로 선언하는것이 두 자료구조간의 거리를 좁혀줍니다.
그래서 사용한 것이구요. 레지스터의 활용은 컴파일러의 적절한 스펙에의해
조절됩니다. 모자르거나 하는일은 발생하지 않습니다.

범용레지스터의 개수가 제한되어 있으니 꼭 필요한 것만 register 지시자를 붙이라는 말씀 같네요.
저렇게 다 붙어 있으면 결국 안붙어 있는 것과 같겠죠. (추측성 발언입니다;; )

Quote:

음수의 표현범위라는 말이 애매한데요. 표현 범위 자체는 일정합니다.
어차피 255 까지 표현가능하다면 MSB 나 LSB 의 bit 하나를 sign bit 으로
사용하기 때문에 표현범위는 일정한 것이지요. 다만 기준이 0 이냐
- 냐의 차이구요.

만약 unsigned char 형의 경우 255 까지 표현가능하고 char 의 경우
-127 ~ +127 이기때문에 표현범위는 일정하지요.
둘다 255니까요. 엄밀히말하면 소수점이 잘리므로 음수가 표현범위가
더 적지요? 254니까요. ^^

그러므로 l = -l 이라는 표현에는 이상이 없습니다.


정수형 타입은 2의 보수로 저장됩니다. 음수쪽이 절대값으로 1만큼 크죠.
%d로 출력할 때 인자로 -(2^31) 를 넣어 테스트 해보세요.
doldori의 이미지

Anonymous wrote:

만약 unsigned char 형의 경우 255 까지 표현가능하고 char 의 경우
-127 ~ +127 이기때문에 표현범위는 일정하지요.
둘다 255니까요. 엄밀히말하면 소수점이 잘리므로 음수가 표현범위가
더 적지요? 254니까요. ^^

그러므로 l = -l 이라는 표현에는 이상이 없습니다.


2의 보수 표현에서는 signed char의 범위가 보통 -128 ~ 127로 알고 있습니다.
따라서 printf_("%d", INT_MIN) 같은 것은 제대로 처리가 안되겠는데요.

정수형이야 그렇다 치고 부동형은 어떻게 처리해야 할지...
수학 함수들 없이는 장난이 아니겠네요.

임베디드 시스템에 대해서는 전혀 모르는 관계로 요기까지만... ^^;

dopesoul의 이미지

조언 감사합니다. :-)

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.