2015-09-08 13 views
5

Jestem obecnie programowanie własną implementację printf, oto kod:Dobre maniery programowania C

int my_printf(const char *format, ...) 
{ 
     //Declare variable ou prog fonctionnel???? 
     va_list args; 
     int numberValue; 
     unsigned int unsignedNumberValue; 
     double doubleValue; 
     char* stringValue; 
     char charValue; 
     va_start(args, format); 
     for(int i = 0; format[i]; i++){ 
       //This case is the most called case, so we test it first to get the 
       //best perfs 
       if(format[i] != '%'){ 
         my_put_char(format[i]); 
       }else{ 
         if(format[i+1] == 'd' || format[i+1] == 'i'){ 
           numberValue = va_arg(args, int); 
           my_put_nbr(numberValue); 
         }else if(format[i+1] == 'u'){ 
           unsignedNumberValue = va_arg(args, unsigned int); 
           my_put_nbr_unsigned(unsignedNumberValue); 
         }else if(format[i+1] == 'o'){ 
           numberValue = va_arg(args, int); 
           my_put_nbr_base(numberValue, 2); 
         }else if(format[i+1] == 'c'){ 
           //char parameters are passed as integer as va_args 
           charValue = va_arg(args, int); 
           my_put_char(charValue); 
         }else if(format[i+1] == 's'){ 
           stringValue = va_arg(args, char*); 
           my_puts(stringValue); 
         }else if(format[i+1] == 'f'){ 
           doubleValue = va_arg(args, double); 
           my_put_double(doubleValue); 
         }else if(format[i+1] == '%'){ 
           my_put_char('%'); 
         }else{ 
           //error option not handled 
           char* error = my_strcat("\nThe option you provided is not a valid option: %", &format[i + 1], 1); 
           write(2, error, sizeof(char) * my_strlen(error)); 
         } 
         i++;  
       } 
     } 
     va_end(args); 
     return 0; 
} 

Chciałbym wiedzieć dwie rzeczy na temat mojego kodu:

  • Zastanawiam się, czy powinienem kod "funkcja" programowania takich jak nie używać zmiennej i umieścić va_args o nazwie bezpośrednio w funkcji my_put. Ale robiąc to, przydzieli i zwolni każdą pętlę zamiast jednej deklaracji (alokacji), która może nie być użyta, a jedna wolna na końcu funkcji. W dużym procesie, przypuszczam, że lepiej jest zapisać zwracaną wartość w zmiennej, a następnie wywołać funkcję "funkcyjną". (dla strlen na bardzo dużym tekście na przykład)
  • Chciałbym również wiedzieć, jak radzić sobie z funkcjami, które można wywołać z różnymi typami, takimi jak my_put_nbr w moim kodzie, czy nie mogę ich przeciążać? Wygląda na to, że musi mieć inną nazwę.
+2

Przepraszam, nie wiedziałem o stronie CodeReview, czy powinienem przenieść mój post? – zyrkiem64

+1

C nie ma przeciążenia funkcji. –

+0

Jeśli chodzi o twoje pierwsze pytanie, jeśli nie chcesz zmiennej pośredniej, po prostu napisz 'my_put_double (va_arg (args, double));'. 'va_arg (...)' jest zwykłym wyrażeniem; nie musisz go używać w oświadczeniu przydziału. Ogólnie rzecz biorąc, warto rozważyć użycie instrukcji 'switch' zamiast długiego łańcucha' if ... else if ... '. Przełącznik jest bardziej czytelny, a czasami szybszy. – rici

Odpowiedz

0

Optymalizacja printf dla prędkości nie ma większego sensu, ponieważ dominującym czasem wykonania będzie znak I/O. Nawet błyskawiczne przetwarzanie formatu nie oszczędza czasu potrzebnego na wysyłanie postaci po drodze.

Jedyna rzecz, na którą warto tutaj zyskać, to rozmiar: rozmiar stosu poprzez unikanie kolejnego poziomu wywołania funkcji i rozmiaru kodu poprzez pisanie jak najmniejszej liczby funkcji wyjściowych.

Pierwszą rzeczą, którą należy zrobić, aby zoptymalizować kod, jest przekształcenie testów w przełącznik. Istnieje szansa, że ​​kompilator zastosuje go jako skok do tabeli, który jest bardziej wydajny niż zestaw testów. Najpierw można umieścić formaty "najbardziej prawdopodobne", ale jeśli nie można odczytać umysłu użytkownika, raczej trudno przewidzieć, czy będzie on raczej wyświetlał liczby całkowite, zmienne lub znaki.

Przekazywanie zmiennej liczby parametrów jest najdroższą operacją, więc przekazanie przetwarzania vararg do każdej funkcji formatowania atomowego spowodowałoby tylko utworzenie większego i wolniejszego kodu.

Zamiast tego, lepiej byłoby napisać swój kod wyjściowy jako funkcje inline i mieć nadzieję, że kompilator rozszerzy je, aby oszczędzić Ci kolejnego poziomu wywołania funkcji.

Dla różnych możliwych rozmiarów argumentów, należy przejść do wszystkich wartości w największym możliwym rozmiarze (32 lub 64 bity całkowite, zmienne lub podwójne w zależności od tego, co ma być obsługiwane) i mieć ograniczoną liczbę funkcji wykonaj wyświetlenie (liczba całkowita ze znakiem/bez znaku liczba całkowita/podwójna dla przykładu).
W ten sposób zmniejszy się zarówno źródłowy, jak i wykonywalny rozmiar kodu.

+0

Miałem zamiar powiedzieć, że najszybsze operacje powinny być najpierw przetwarzane, ale właśnie to OP wykonało w większości. Godny uwagi wyjątek to '% c'. –

+0

To i tak będzie zaniedbywalne. Przełącznik najprawdopodobniej zostanie zaimplementowany jako skok wyszukiwania tabeli lub dowolna struktura, którą kompilator uzna za bardziej wydajną. W porównaniu z resztą przetwarzania, to nic nie zyska. –

+0

Nie mój downtoet, ale wydajesz się zaprzeczać sobie, mówiąc, że metoda przetwarzania zyska niewiele. –