2009-01-31 12 views
3

나는 printf와 아이디어를 가지고 에 일반 printf 을 호출하는 my_printf (...)와 그 결과를 특별한 함수로 보내는 sprintf를 작성하려고합니다. (나는 대부분의 플랫폼에서 printf와 비슷한 동작을하기 때문에 sprintf에 대해 생각해 보았습니다).sprintf와 일반 printf 모두에 데이터를 보내는 my_printf를 만드시겠습니까?

내 생각은 이런 짓을 작은 매크로를 작성했다 :

 
#define my_printf(X, Y...) do{ printf(X, ## Y); \ 
    char* data = malloc(strlen(X)*sizeof(char)); \ 
    sprintf(data, X, ## Y); \ 
    other_print(data);\ 
    free(data);}while(0) 

을하지만 sprintf와이 X보다 훨씬 더 큰 크기로 문자열을 확장 할 수 있기 때문에, 이 방법은 거의 직접 나누기.

그리고 단지 malloc이 문제를 공격하는 잘못된 방법 인 것 같습니다. 그 이후로 나는 큰 식을 인쇄하고 싶을 때 미래와 하루로 문제를 옮길 것입니다 ...

누구든지이 문제를 공격하는 방법에 대해 더 좋은 아이디어가 있습니까? 또는 sprintf 결과가 얼마나 클지 알 수 있습니까?

감사 요한


업데이트 : 나는 printf와 반환 얼마나 많은 문자가, 을 인쇄하고 이미 매크로에서의 printf를 호출되기 때문에 그것을 을 추가하는 매우 쉬운 일이었다 잊었 int를 저장합니다.

 
#define buf_printf(X, Y...) do{ int len = printf(X, ## Y); \ 
    char* data = malloc((len+1)*sizeof(char)); \ 
    sprintf(data, X, ## Y); \ 
    other_print(data);\ 
    free(data);}while(0) 

업데이트 : 나는 이것에 대해 생각하고 아마 좋은 생각이 무엇인지 ephemient 제안했다처럼 많이 보이는 정상적인 기능 를 사용 할 수 있습니다. 열쇠는 다른 printf 함수 v-version 인 (vprintf, vsprintf 및 vsnprintf) 인 것 같습니다. 그 점을 지적 해 주셔서 감사합니다.

다시 한번 감사 요한

답변

5

이 작업을 수행하는 가장 좋은 방법은 가변 인자와 함께입니다. printf()와 같은 프로토 타입으로 함수를 만들고 varargs 함수를 사용하여 데이터를 sprintf에 전달하여 원하는 버퍼를 채우고 반환 할 버퍼를 printf ("% s")로 전달합니다.

많은 초기 구현에는 최저 수준의 printf() 호출에 4K 제한이 있었지만 그 이상을 선택했습니다. 아마도 상한선을 정하고이를 고수해야 할 것입니다.

우리가 로깅 시스템에서 사용한 한 가지 트릭은 printf()를 사용하여/dev/null 핸들에 데이터를 쓰는 것이 었습니다. printf()는 쓰여진 문자의 수를 반환하기 때문에 우리는 그것을 사용하여 버퍼를 할당했다. 하지만 printf() 유형의 함수를 두 번 호출했기 때문에 그렇게 효율적이지 않았습니다.

+0

불쾌한 매크로는 피할 수 없으므로 동의합니다.하지만 할당 할 버퍼가 얼마나 큰지 문제가있는 질문자에게 도움이된다고 생각하지 않습니다. –

+0

edit는 이전의 코멘트를 false로 렌더링합니다 :-) –

+0

예, sprintf()처럼 myprintf()의 호출자가 이미 충분히 큰 버퍼를 가지고 있다고 가정하여 질문을 잘못 읽었습니다. – paxdiablo

8

snprintf를 사용하여 크기를 계산하십시오. 맨 페이지에서 :

"출력이이 한계 때문에 잘린 경우 반환 값은 충분한 공간이 있으면 마지막 문자열에 쓰여진 문자 수 (후행 '\ 0'을 포함하지 않음)입니다. 사용 가능 "

snprintf는 C99의 표준입니다. C89 컴파일러 만 있으면 문서를 확인하십시오. 사전 표준 버전에서는 원하는 값을 반환하지 않을 수 있습니다.다시 man 페이지에 따르면 버전 2.1 이전의 glibc는 필요한 크기가 아닌 출력이 잘린 경우 -1을 반환했습니다. 길,를 sizeof (char)로

는 지금까지 :-)

+0

sizeof (char) == 1 항상 흥미 롭습니다. 그러나 나는 항상 그것을 거기에 두어 다른 사건들에서 그것을 잊지 않도록한다. – Johan

+0

또한 먼저 버퍼에 snprintf를 작성한 후 버퍼를 인쇄하십시오. 그런 식으로 문자열은 한 번만 포맷됩니다. –

+0

@David - 나는 sprintf(), puts()를 의미한다고 생각합니다. 흥미롭게도 GCC는 가능한 경우 puts()에 printf ("% s", foo)를 최적화합니다. – Tom

2

당신은 항상 glibc (즉, 리눅스 및 기타 OS와 시스템에서 실행하는 경우 모든 C 구현에 항상 1로 정의된다 GNU 사용자 영역에서) asprintfsprintf처럼 동작하지만 할당 자체를 자동으로 처리 할 수 ​​있습니다.

int my_printf(const char *fmt, ...) { 
    char *buf = NULL; 
    int len; 
    va_list ap; 

    va_start(ap, &fmt); 
    len = vasprintf(&buf, fmt, ap); 
    va_end(ap); 

    if (len < 0) { 
     /* error: allocation failed */ 
     return len; 
    } 

    puts(buf); 
    other_print(buf); 

    free(buf); 
    return len; 
} 

인원의 대답은 더 휴대용, 대신 인쇄 /dev/null로, 여기에 더 나은 트릭이다 : POSIX에 의해, snprintfNULL 버퍼와 0 크기를 제공 할 수 있으며, 그것을 기록했을 정도 돌아갑니다 -하지만 실제로는 아무 것도 쓰지 않을 것입니다. onebyone 말한대로

int my_printf(const char *fmt, ...) { 
    char *buf; 
    int len, len2; 
    va_list ap; 

    va_start(ap, &fmt); 
    len = vsnprintf(NULL, 0, fmt, ap); 
    va_end(ap); 

    buf = malloc(len + 1); 
    if (!buf) { 
     /* error: allocation failed */ 
     return -1; 
    } 

    va_start(ap, &fmt); 
    len2 = snprintf(buf, len + 1, fmt, ap); 
    buf[len] = '\0'; 
    va_end(ap); 

    /* has another thread been messing with our arguments? 
     oh well, nothing we can do about it */ 
    assert(len == len2); 

    puts(buf); 
    other_printf(buf); 

    free(buf); 
    return len; 
} 

음, 오래된 시스템은 준수 snprintf이 없습니다. 그들 모두를 이길 수는 없어 ...

5

리눅스를 사용하고 있으므로 asprintf()을 사용하는 것이 좋습니다. 문자열을 할당하는 GNU 확장을 사용하는 것이 좋습니다. C99 가변 매크로 덕분에 가변 작업을 망칠 필요가 없습니다.

#define MY_PRINT(...) do { \ 
          char *data; \ 
          asprintf(&data, __VA_ARGS__); \ 
          printf("%s", data); \ 
          other_print(data); \ 
          free(data); \ 
         } while (0) 

NB :

그래서 매크로과 같을 것이다! 이것은 C99와 GNU 전용 코드를입니다

편집 : 지금 그냥 한때 같은 매크로 호출 매크로 인수를 평가합니다 ("% d 개"를, 내가 ++) 제대로 작동합니다.

+0

Typo : s/aspintf/asprintf /. 나는 개인적으로 이것을 다루는 매크로 방식을 좋아하지 않는다. 왜냐하면 여러 인자에 대한 평가가 나오기 때문이다. 그러나 확실히 더 단순 해 보인다. – ephemient

+0

여러 가지 부작용을 피하기 위해 메모를 주셔서 감사합니다. 오타 수정, 매크로가 변경되었습니다. – qrdl

+0

죄송합니다. 'printf (data)'는'printf ("% s", data)'또는'puts (data)'에 비해 나쁘다 :'data'에'%'가 포함되어 있다면? – ephemient