Как написать свой собственный printf () в C? - PullRequest
12 голосов
/ 14 ноября 2009

На самом деле я пытаюсь написать свой собственный printf() на C, используя varags. Но я не получаю правильное решение для этого. Кто-нибудь может мне помочь?

Ответы [ 5 ]

11 голосов
/ 18 февраля 2015

Перед реализацией функции printf () нам приходится сталкиваться с необычной проблемой - переменными аргументами. Как мы знаем, printf может принимать множество аргументов помимо строки. Таким образом, мы должны использовать стандартную библиотеку stdarg.h для решения этой проблемы с переменным аргументом. В этом контексте реализации нам не нужно изучать всю библиотеку stdarg.h, потому что мы используем некоторые макрофункции этой библиотеки, что понятно для нашей программы на C

Вот источник кода, который объясняет быстро и красиво

#include<stdio.h> 
#include<stdarg.h>                      

void Myprintf(char *,...);              //Our printf function
char* convert(unsigned int, int);       //Convert integer number into octal, hex, etc.


int main() 
{ 
    Myprintf(" WWW.FIRMCODES.COM \n %d", 9); 

    return 0;
} 


void Myprintf(char* format,...) 
{ 
    char *traverse; 
    unsigned int i; 
    char *s; 

    //Module 1: Initializing Myprintf's arguments 
    va_list arg; 
    va_start(arg, format); 

    for(traverse = format; *traverse != '\0'; traverse++) 
    { 
        while( *traverse != '%' ) 
        { 
            putchar(*traverse);
            traverse++; 
        } 

        traverse++; 

        //Module 2: Fetching and executing arguments
        switch(*traverse) 
        { 
            case 'c' : i = va_arg(arg,int);     //Fetch char argument
                        putchar(i);
                        break; 

            case 'd' : i = va_arg(arg,int);         //Fetch Decimal/Integer argument
                        if(i<0) 
                        { 
                            i = -i;
                            putchar('-'); 
                        } 
                        puts(convert(i,10));
                        break; 

            case 'o': i = va_arg(arg,unsigned int); //Fetch Octal representation
                        puts(convert(i,8));
                        break; 

            case 's': s = va_arg(arg,char *);       //Fetch string
                        puts(s); 
                        break; 

            case 'x': i = va_arg(arg,unsigned int); //Fetch Hexadecimal representation
                        puts(convert(i,16));
                        break; 
        }   
    } 

    //Module 3: Closing argument list to necessary clean-up
    va_end(arg); 
} 

char *convert(unsigned int num, int base) 
{ 
    static char Representation[]= "0123456789ABCDEF";
    static char buffer[50]; 
    char *ptr; 

    ptr = &buffer[49]; 
    *ptr = '\0'; 

    do 
    { 
        *--ptr = Representation[num%base]; 
        num /= base; 
    }while(num != 0); 

    return(ptr); 
}
6 голосов
/ 14 ноября 2009

Если у вас есть время и вам действительно любопытно, вы можете изучить версию GNU libc: См. printf , который в свою очередь использует vprintf, который использует vfprintf

3 голосов
/ 15 ноября 2009

Есть как минимум две книги с хорошими объяснениями того, как можно написать printf() -подобную функцию форматирования (и полные рабочие примеры):

3 голосов
/ 14 ноября 2009

Страница руководства Linux va_start (3) дает очень хороший пример написания таких функций (гораздо проще, но в целом все основные кирпичики есть). Также вы можете изучить практически любую реализацию libstdc.

1 голос
/ 15 ноября 2009

Этот ответ может помочь вам понять, как писать переменные функции. Обратите внимание, что проверка ошибок / границ не выполняется, не заданы атрибуты, указывающие компилятору, какие аргументы могут подойти, никакого преимущества над простым использованием printf () не достигается.

Это может быть или не быть примером, который вы ищете.

Соответствующий фрагмент (здесь немного расширен):

#include <stdarg.h>
void _printf(FILE *out, va_list ap)
{
    vfprintf(out, fmt, ap);
}

void printf(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    _printf(stdout, ap);
    va_end(ap);
}

Примечание: для того, чтобы они возвращали правильный тип (целое число со знаком), оставляем читателю упражнение. Для меня это выглядит как домашнее задание, я просто пытаюсь избавить вас от любой проблемы с использованием va_start и va_end, а также показывает, что va_list можно передавать вспомогательным функциям, чтобы избежать дублирования кода во многих реализациях почти то же самое.

Я настоятельно рекомендую посмотреть на реализацию подсистемы BSD (или даже glibc) printf. Вы также можете посмотреть на uclibc, dietlibc и т. Д.

...