printf


printf gibt formatierte Ausgaben auf dem Standardausgabekanal aus. Bei Verwendung eines PC-Betriebssystems ist der Standardausgabekanal die Konsole / Terminal. Bei Verwendung mittels eines Mikrocontrollers muss je nach benutzter Hardware ein Ausgabekanal geschaffen werden, bpsw. ein angeschlossenes Display, eine Ausgabe über die serielle Schnittstelle oder anderes.

Bei Verwendung mit einem Mikrocontroller ist zu überdenken, ein "full featured" printf einzusetzen, da printf sehr speicherintensiv ist, besonders in Verbindung mit der Ausgabe von Gleitkommazahlen. Ein im Leistungsumfang reduzierter, aber in vielen Fällen ausreichender Ersatz für printf ist my_printf.

    Syntax von printf      : int printf(const char *format [, argument, ...]);
    Prototypdeklaration in : stdio.h
printf unterscheidet sich im Gegensatz zu den meisten Funkktionen in C darin, dass für diese Funktion eine nicht definierte (variable) Anzahl von Funktionsparametern (argument) übergeben werden kann. Bei fehlerfreier Ausfuehrung liefert printf die Anzahl der insgesamt ausgegebener Zeichen als Funktionsargument zurück.
    const char *format
enthält den zwingend notwendigen String, der ausgegeben werden soll. Alle eventuell nachfolgende Funktionsparameter sind optional.

Beispiel:
    printf("Hallo Welt");
Innerhalb des Strings *format können Umwandlungszeichen (engl. conversion modifier) für weitere Parameter eingesetzt werden, die eine
Formatierung der Ausgabe ermöglichen. Außerdem kann dieser String
Escape-Sequenzen enthalten, die ebenfalls der formatierten Ausgabe dienen.


Umwandlungszeichen %


Prinzipiell funktionieren die Umwandlungszeichen nach folgendem Schema: Ein Umwandlungszeichen wird mit dem %-Zeichen (Ascii-Code 37) als Einleitungszeichen angegeben.

Direkt anschließend an dieses Einleitungszeichens können weitere Angaben folgen:

  • ein Flagzeichen (erlaubte Zeichen: SPACE,-,+,#,0)
  • die Feldbreite
  • durch einen Punkt getrennt die Anzahl der Nachkommastellen
  • an letzter Stelle das Umwandlungszeichen (und somit den Ausgabetyp)

Ein einfaches Beispiel nur mit Umwandlungszeichen (ohne Flags, ohne Feldbreite und ohne Punkt):
    printf("1. Zahl= %d, 2. Zahl= %d, 3. Zahl= %d",34, 65, 99);
Ausgabe:
    1. Zahl= 34, 2. Zahl= 65, 3. Zahl= 99
Erklärung:

Da im Ausgabestring insgesamt 3 Umwandlungszeichen enthalten sind (3 mal %d), werden diese %d durch die jeweils folgenden Parametern ersetzt. D.h. anstelle des ersten im String enthaltenen %d wird die dem %d zugeordneter Parameter ausgegeben. Im vorliegenden Falle wird also in den Text die Zahl 34 mit dezimaler Ausgabe eingefügt. Anstelle des zweiten Vorkommens von %d wird 65, anstelle des dritten wird 99 ausgegeben.


Verfügbare Umwandlungszeichen sind:


     Zeichen 
    Datentyp und Darstellung

    %d
    Integer als dezimale Zahl ausgeben
    %i
    Integer als dezimale Zahl ausgeben
    %c
    Ausgabe eines char als (Ascii)-Zeichen
    %e
    Ausgabe einer double Gleitkommazahl im wissenschaftlichen Format (bspw.: 3.342409e+3)
    %E
    Ausgabe einer double Gleitkommazahl im wissenschaftlichen Format (bspw.: 3.342409E+3)
    %f
    Ausgabe einer double Gleitkommazahl (bspw.: 33424.092342)
    %o
    oktale Ausgabe eines Integers
    %p
    Ausgabe der Adresse eines Pointers
    %s
    Ausgabe einer Zeichenkette (String)
    %x
    Integer als hexadezimale Zahl ausgeben, alphanumerische Ziffern werden klein geschrieben
    %X
    Integer als hexadezimale Zahl ausgeben, alphanumerische Ziffern werden groß geschrieben
    %u
    Ausgabe eines vorzeichenlosen Integers
    %%
    Ausgabe des Prozentzeichens

Beispiele:
    printf("int : %d \n", 65);
    printf("negative int : %d \n",-65);
    printf("Gleitkomma : %.6f \n", M_PI);
    printf("Asciizeichen 65 : %c \n", 'z');
    printf("Zeichenkette : %s \n", "AbCdEf");
    printf("43 in Oktal : %o\n", 65); printf("43 in Hex : %x\n", 65); printf("Prozentzeichen : %%\n");
Ausgabe:
    int : 65
    negative int : -65
    Gleitkomma : 3.141593
    Asciizeichen 65 : z
    Zeichenkette : AbCdEf
    43 in Oktal : 101
    43 in Hex : 41
    Prozentzeichen : %


Flag / Flagzeichen


Direkt nach dem %-Einleitungszeichen kann ein sogenanntes Flag-Zeichen (Kennzeichnung) angegeben werden. Dieses Zeichen beeinflußt die Ausgabe auf dem Ausgabekanal.

     Zeichen  Bedeutung

    -
    Text wird linksbuendig ausgerichtet
    +
    auch bei einem positiven Zahlenwert wird ein Vorzeichen mit ausgegeben
    SPACE
    Leerzeichen, ein Leerzeichen wird ausgegeben, wenn der Wert positiv ist
    #
    abhängig von Format und Betriebssystem. In Verbindung mit hexadezimaler Ausgabe wird der Ausgabe "0x" vorangestellt.
    0
    eine Auffüllung erfolgt mit Nullen anstelle von Leerzeichen
Beispiel:
    printf("Hex-Zahl: %#x\n", 76);
Ausgabe:
    Hex-Zahl: 0x4c


Feldbreite und Nachkommastellen


Nach den Flags können Angaben zur Gesamtzeichenanzahl der Ausgabe (Feldbreite) und / oder Angabe zur Anzahl der Nachkommastellen gemacht werden.

Wird eine für die Gesamtbreite eine höhere Anzahl Stellen angegeben, als die auszugebende Zahl selbst besitzt, so wird die Ausgabe linksseitig um Leerstellen aufgefüllt, so dass die geforderte Gesamtbreite erfüllt ist.

Ist als Flag die Ziffer "0" angegeben, so wird die Gesamtausgabe mit Nullen anstelle von Leerzeichen aufgefüllt. Soll eine Gleitkommazahl ausgegeben werden (float oder double), so kann die Feldbreite durch ein "." Zeichen auch den Nachkommateil beinhalten. Hierbei gibt die Angabe für die Feldbreite die Gesamtzeichenzahl der Ausgabe an, die Ziffernangabe nach dem Punkt die Anzahl der Stellen des Nachkommateils.

Überschreitet die Anzahl der Zeichen für den Nachkommateil die Angabe für die Gesamtzeichenanzahl, so werden jedoch die Zeichen für den Nachkommateil garantiert ausgegeben, so dass die Gesamtzeichenanzahl der Ausgabe in diesem Fall die der angegebenen
Feldbreite überschreitet.

Der Punkt, der den Vorkommateil vom Nachkommateil trennt zählt hierbei als Zeichen für die Gesamtbreite. Es kann auch nur die Anzahl der Nachkommastellen ohne führende Gesamtanzahl angegeben werden.

Beispiele:
    printf("\n1234567890");
    printf("\n%10d",23);
    printf("\n%010d",65);
    printf("\n%10.4f",M_PI);
    printf("\n%.3f",M_PI);
    printf("\n");
Ausgabe:
    1234567890
            23
    0000000065
        3.1416
    3.142


Escape-Sequenzen / Steuerzeichen



Der Ausgabestring kann sogenannte Steuerzeichen (Escape-Sequenzen) enthalten. Das Ausgabeverhalten hängt vom verwendeten Terminal bzw. der verwendeten Terminalemulation ab (die Windowskonsole verhält sich leider abweichend zur Linuxkonsole).

Ein Steuerzeichen wird durch einen Schrägstrich nach rechts unten (engl. backslash) "angemeldet".


    Zeichen
    Bedeutung

    \n
    (new line) fügt eine neue Zeile unterhalb der aktuellen Zeile ein. Hierbei ist das Ausgabeverhalten von der Terminalemulation abhängig. Die Linuxkonsole generiert bei einem "new line" Kommando auch ein Bewegen des Cursors an den Zeilenanfang (was einem "carriage return" entspricht). Bei der Windowskonsole bleibt der Cursor in der x-Position verharren, an der er sich befindet. Ein "/n" unter Linux bewirkt somit dasselbe wie ein "/n/r" unter Windows.
    \r
    (carriage return) bewirkt ein Setzen des Cursors an den Zeilenanfang
    \t
    setzt den Cursor auf die nächste horizontale Tabulatorposition
    \b
    setzt den Cursor ein Zeichen nach links OHNE das aktuelle Zeichen hierbei zu löschen
    \f
    setzt den Cursor auf die Startposition der nächsten Seite
    \v
    setzt den cursor auf die nächste vertikale Tabulatorposition
    \0
    markiert das Ende einer Textzeile (nachfolgende Zeichen werden nicht mehr interpretiert und auch nicht ausgegeben

Beispiel (unter Verwendung von \n \r t und Flags):

    // Hinweis: \r Escape-Sequenz waere fuer Programmlauf unter Linux nicht notwendig

    printf("\n\rQuadratzahlen\t Wurzel\t\t  Reziprokenwerte");
    printf("\n\r---------------------------------------------------\n\r");
    for (i= 1; i< 17; i++)
    {
      printf("\n\r %2d * %2d= %4d\t sqrt(%2d)= %4.2f\t  1/%2d= %.4f",         \
                    i,    i,  i*i,    i,   sqrt((float)i), i,  1.0/(float)i);
    }

Ausgabe:
 
Quadratzahlen    Wurzel           Reziprokenwerte
---------------------------------------------------

   1 *  1=    1   sqrt( 1)= 1.00   1/ 1= 1.0000
   2 *  2=    4   sqrt( 2)= 1.41   1/ 2= 0.5000
   3 *  3=    9   sqrt( 3)= 1.73   1/ 3= 0.3333
   4 *  4=   16   sqrt( 4)= 2.00   1/ 4= 0.2500
   5 *  5=   25   sqrt( 5)= 2.24   1/ 5= 0.2000
   6 *  6=   36   sqrt( 6)= 2.45   1/ 6= 0.1667
   7 *  7=   49   sqrt( 7)= 2.65   1/ 7= 0.1429
   8 *  8=   64   sqrt( 8)= 2.83   1/ 8= 0.1250
   9 *  9=   81   sqrt( 9)= 3.00   1/ 9= 0.1111
  10 * 10=  100   sqrt(10)= 3.16   1/10= 0.1000
  11 * 11=  121   sqrt(11)= 3.32   1/11= 0.0909
  12 * 12=  144   sqrt(12)= 3.46   1/12= 0.0833
  13 * 13=  169   sqrt(13)= 3.61   1/13= 0.0769
  14 * 14=  196   sqrt(14)= 3.74   1/14= 0.0714
  15 * 15=  225   sqrt(15)= 3.87   1/15= 0.0667
  16 * 16=  256   sqrt(16)= 4.00   1/16= 0.0625 


sprintf



sprintf verhält sich genau wie printf mit dem Unterschied, dass die Ausgabe nicht auf dem Standardausgabekanal, sondern in einen String (d.h. in ein Char-Array) erfolgt.

    Syntax von sprintf     : int sprintf(char *buffer, const char *format [, argument, ...]);

    Prototypdeklaration in : stdio.h
Sämtliche Angaben fuer Umwandlungszeichen, Flags sowie Feldbreitenbezeichner die für printf gelten, sind auch für sprintf gültig.

Beispiel:

  char  txtbuffer[100];
  
  sprintf(txtbuffer, "\n%.3f * sqrt(%.1f) = %.3f\n", M_PI, 2.0, M_PI * sqrt(2.0));
  printf("%s", txtbuffer);
Ausgabe:
    3.142 * sqrt(2.0) = 4.443

my_printf



my_printf ist ein in der Funktionalität reduzierter Ersatz für den jedem C-Compiler beiliegenden printf und ist speziell für die Benutzung in Verbindung mit Mikrocontrollern gedacht. my_printf macht immer dann Sinn, wenn es darum geht Flashspeicher zu sparen und ermöglicht den Einsatz auf Mikrocontrollersystemen ab ca. 2 kByte Flashspeicher.
    Die Syntax von my_printf : void my_printf(const char *s,...);
    Prototypdeklaration in : ../include/my_printf.h
In der speichersparsamsten Version verfügt my_printf über keine Ausgabemöglichkeit von float oder double, ein jedoch vom Standard abweichendes Umwandlungszeichen %k ermöglicht es, Pseudo-Kommazahlen auszugeben.

my_printf ist gespeichert im Verzeichnis ../src in den Quelltextvarianten:
  • my_printf.c   (für 8 Bit-, 32-Bit- und PC-Systeme)
  • my_printf32.c   (nur für 8-Bit Systeme
  • my_printf_float.c

Um my_printf innerhalb eines Programms zur Verfügung zu haben, muss entsprechend der gewünschten Funktionalität die benötigte Datei mit dem "Hauptprogramm" compiliert und hinzugelinkt werden. Bei Verwendung der hier benutzten Makefile Vorlagen wird dies erreicht durch eine Angabe im Makefile von:

  SRCS += ../src/my_printf.c
 
Die zu den verschiedenen my_printf gehörenden Header-Dateien ist für alle Varianten unabhängig der Funktionalität gleich:

  #include "my_printf.h"

my_printf benötigt zur Ausgabe in einer der zu einem Programmprojekt gehörenden Dateien eine Funktion namens:

  void my_putchar(char ch);

my_printf bedient sich dieser Funktion zur Zeichenausgabe. Soll bspw. ein Programm die Ausgaben auf der seriellen Schnittstelle vornehmen gibt es in einer der Dateien wahrscheinlich eine Funktion ähnlich dieser:

  void uart_putchar(char ch);

Um nun Ausgabefunktion nutzen zu können, könnte ein einfaches Programm folgendermassen aussehen:

  #include <avr/io.h>
 
  #include "uart_all.h"
  #include "my_printf.h"
 
  #define printf    my_printf
 
  /* --------------------------------------------------------
     my_putchar
  
     wird von my-printf aufgerufen und hier muss
     eine Zeichenausgabefunktion angegeben sein, auf das
     printf dann schreibt !
     -------------------------------------------------------- */
  void my_putchar(char ch)
  {
    uart_putchar(ch);
  }
 
  int main(void)
  {
    uart_init();
    printf("\n\r Hallo Welt\n\r");
    while(1);
  }

Im gezeigten Beispiel wird die Zeile
#define printf my_printf dazu genutzt, um im gesamten Programm printf anstelle von my_printf schreiben zu können und somit bereits bestehende Programme mit my_printf (anstelle von printf aus stdio.h) verwenden zu können. Die originale printf Funktion ist nach diesem define nicht mehr verfügbar.


Umwandlungszeichen my_printf


     Zeichen 
    Datentyp und Darstellung

    %d
    Integer als dezimale Zahl ausgeben (int ist 16-Bit Wert auf einem 8-Bit System, ein 32-Bit Wert auf einem 32-Bit Systemen
    %l
    nur bei printf32.c : 32-Bit Integerwert ausgeben (verfügbar für AVR, STM8 und MCS-51)
    %c
    Ausgabe eines char als (Ascii)-Zeichen
    %k
    (Pseudo-Kommazahl) Integerwert mit eingesetztem Dezimalpunt ausgeben, auf 32-Bit Systemen oder bei my_printf32 als 32-Bit Wert, ansonsten als 16-Bit Wert. Mit dem Wert in der globaler Variable printfkomma wird angegeben, an welcher Position bei der Ausgabe ein Dezimalpunkt eingefügt wird.
    %f
    nur bei my_printf_float.c, Ausgabe eines (float) Gleitkommawertes. Ein Punkt zur Angabe der Nachkommastellen wird interpretiert
    %s
    Zeichenkette (String) ausgeben
    %x
    Integer als hexadezimale Zahl ausgeben
    %%
    Ausgabe des Prozentzeichens


Beispiel:

#define printf    my_printf
 
  int a,a2,b,c;
 
  printf("\n\r ASCII-Zeichen '%c' = dezimal %d = hexadezimal 0x%x\n\r", 'M', 'M', 'M');
 
  a= 42; b= 13;
 
  // Variable a um eine Zehnerstelle nach links,
  // um spaeter eine Nachkommastelle anzeigen zu koennen
  a2= a*100;
  c= a2 / b;
 
  printfkomma= 2;
  printf(" %d / %d = %k", a, b, c);

Ausgabe:
    ASCII-Zeichen 'M' = dezimal 77 = hexadezimal 0x4D
    42 / 13 = 3.23