Autor Thema: kprintf %d usw ersetzen  (Gelesen 10995 mal)

jo2105

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« am: 11. November 2012, 12:08 »
Hi Leute.

Ich bin gerade dabei, meine kprintf-Funktion so zu erweitern, dass man wie im Tutorial gefordert soetwas machen kann: kprintf("Die Zahl ist %d", 5);
Ich bin mir jetzt nicht so sicher wie man 1. Integer in chars umwandeln kann, sodass auch wirklich die Zahl später angezeigt wird, nicht das zugehörige Zeichen, und 2. wie man das %d durch die Zahl ersetzt.

Meine aktuelle for-Schleife sieht so aus:
  for(i=0; string[i] != '\0'; i++, iOff++) {
    if(string[i] == '\n') {
iCursor += 1;
iOff = -1;
    } else if(string[i] == '%') {
if(string[i+1] == 'd') {
   int iArg = va_arg(ap, int);
} else {
   video[(iCursor*160)+iOff*2] = string[i];
   video[(iCursor*160)+iOff*2+1] = 0x07;    
}
    } else {
video[(iCursor*160)+iOff*2] = string[i];
video[(iCursor*160)+iOff*2+1] = 0x07;
    }
  }

Ich hoffe ihr könnt mir da ein paar Tipps geben.  :-)
 

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 11. November 2012, 12:16 »
in dem teil wo du die int abfrags, sieht zwar etwas kaputt aus, switch würde sich wahrscheinlich besser machen, sollte aber funktionieren, muss du noch eine funktion einbauen wie intToChar(int i, int base), das dir ein char array oder anderes construct zurück gibt, dann kannst du einfach sowas machen wie kprint(intToChar(iArg, 10)); machen, musst dann aber danach aufpassen, dass du das i nochmal manuell inkrementieren musst.
Zudem musst du noch irgendwie abfangen, ob ein '%' am ende des strings steht ...

korrigiert mich, wenn ich was falsches gesagt hab :)

mfg
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

jo2105

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 11. November 2012, 12:47 »
in dem teil wo du die int abfrags, sieht zwar etwas kaputt aus, switch würde sich wahrscheinlich besser machen, sollte aber funktionieren, muss du noch eine funktion einbauen wie intToChar(int i, int base), das dir ein char array oder anderes construct zurück gibt, dann kannst du einfach sowas machen wie kprint(intToChar(iArg, 10)); machen, musst dann aber danach aufpassen, dass du das i nochmal manuell inkrementieren musst.
Zudem musst du noch irgendwie abfangen, ob ein '%' am ende des strings steht ...

korrigiert mich, wenn ich was falsches gesagt hab :)

mfg

Danke erstmal für die Antwort. :)

Das mit dem '%' am Ende des Strings wird schon durch das else abgefangen, weil dann string[i+1] == '\0' wäre und dann einfach das Zeichen dahinschreibt.

Wie könnte denn so eine intToChar Funktion aussehen?

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 11. November 2012, 13:16 »
Ok, mir fällt gerade auf, intToChar wäre schonmal blödsinn, eher intToStr xD ... und um es ganz konform zu machen itoa(int data, char[] buffer, int base)

wie das aussehen könnte öhm:
void itoa(int data, char[] buffer, int base)
{
    char[] numc = {'0','1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E','F', ..., 'Z'};
    buffer[sizeof(buffer)-1]='\0';
    int i;
    for(i=sizeof(buffer)-2, i>=0,i--)
    {
        buffer[i]=numc[data%base];
        data=data/base;
    }
}

ist ungetestet und frei aus dem kopf notiert, zudem möchte ich hinzufügen, dass ich recht wenig bisher mit C programmiert habe, also ist der code wahrscheinlich an irgendeiner stelle falsch (btw ... im array numc soll '...' durch die aufzählung von 'G' bis 'Y' ersetzt werden, war nur zu faul um das alles hinzuschreiben) der code sollte dir jede int zahl zu einer basis deiner wahl ausgeben, sogar zu 1 oder 0, was keinen sinn macht und zu 0 sogar fatal wäre! also abfrage reinhauen :) )
ähm ... viel spass.
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

jo2105

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 11. November 2012, 13:23 »
Ah.^^ Vielen Dank. :)

Wieder so eine Sache die eigentlich einfach ist, wo man aber erst drauf kommen muss. :D

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 11. November 2012, 15:31 »
Okay, mal ein paar Tipps:

Ich empfehle eine Funktion, die einfach nur ein Zeichen ausgibt. void kputc(char c) oder so. Diese Funktion beinhaltet die Ausgabe normaler Zeichen, sowie die Verarbeitung von Zeilenumbrüchen (\n) und der ganzen anderen Steuerzeichen (\t, \r, \b, ...), wenn du dich entschließt die zu unterstützen. Durch diese Funktion sollte sich deine kprintf-Funktion vereinfachen, weil dann kprintf nicht mehr direkt auf den Videospeicher zugreifen muss. kprintf sollte nur noch kputc aufrufen und keine anderen globalen Variablen (video, iOff, iCursor) anfassen.

Allerdings kannst du dir noch eine weitere kleine Funktion void kputs(char *string) oder so bauen, die nichts anderes macht als den String komplett auszugeben. Das sollte eine einfache Schleife sein, die nur kputc aufruft. Diese Funktion ist zum Beispiel nützlich, wenn du %s im kprintf verarbeiten willst. Oder wenn du einfach nicht kprintf aufrufen willst.

Die itoa-Funktion von chris12 funktioniert in C nicht. sizeof darf man nicht auf diese Art von Parametern verwenden. Die klassische itoa-Funktion nimmt einen char*-Parameter für buffer und hofft einfach, dass dieser groß genug ist (sonst gibts einen Buffer-Overflow). So könntest du das dann machen:

char *itoa(int number, char *buffer, int base)
{
     char *result = buffer;
     char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";

     // TODO: hier negative Zahlen behandeln, wenn base == 10.

     do {
          buffer++ = digits[number % base];
          number = number / base;
     } while (number != 0)
     *buffer = 0;

     // TODO: string umkehren, sonst ist die Zahl rückwärts

     return result; // Die Standardfunktion gibt den Eingabepuffer wieder zurück.
}
« Letzte Änderung: 11. November 2012, 15:33 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 11. November 2012, 19:52 »
@Jidder hmm laut http://home.fhtw-berlin.de/~junghans/cref/SYNTAX/sizeof.html sollte sizeof so funktionieren ... aber ich nehme einfach mal an, dass ich da was falsch interpretiert hab :D
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 11. November 2012, 20:30 »
sizeof funktioniert in dem Beispiel, weil bei char String[20] die Größe explizit angegeben ist. Bei dem Parameter char[] buffer; ist die Größe hingegen nicht zur Compilezeit bekannt. sizeof kann nur die Größen ermitteln, die auch anders ermittelt werden könnten. sizeof kann auch für dynamische Arrays auf dem Stack verwendet werden, allerdings ist da die Größe zur Laufzeit bekannt.

char x[20]; // sizeof(x) = 20 -> steht dran
char y[] = { 'a', 'b', 'c' }; // sizeof(y) = 3 -> kann man auch abzählen
void foo(int n) {
  char z[n + 3]; // sizeof(z) = n + 3 -> hätte man auch selbst ausrechnen können
}

Parameter, die als einfache Arrays deklariert werden, werden hingegen zu Zeigern:
void foo(char z[]) // sizeof(z) = sizeof(char*)
Dieser Text wird unter jedem Beitrag angezeigt.

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 11. November 2012, 21:39 »
ah ... der nachteil wenn man von einer sprache mit laufzeitunterstützung auf eine ohne umdenken muss ;) ... aber danke für die erklährung :D
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

jo2105

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 12. November 2012, 18:15 »
Ich glaube ich werde meine Funktion nochmal so umbasteln, wie es Jidder gesagt hat. Das macht so einiges einfacher, weil man dann nicht an irgendwelchen Strings rumschnippeln muss. :D

jo2105

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 12. November 2012, 20:59 »
Jetzt verstehe ich die Welt nicht mehr. :D

Ich habe jetzt einen ersten Prototyp von putchar gebastelt, der auch soweit funktioniert. Wenn ich die Funktion aber im Header deklariere, sagt mir GCC  "warning conflicting types". Wenn ich die Deklarierung rausnehme, funktioniert es, weil das implizit deklariert wird. Weiß da jemand was man da machen könnte? Ich möchte nicht ewig mit der implicit declaration Warnung kompilieren. :D

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 12. November 2012, 21:09 »
Du solltest immer die gesamte Fehlermeldung lesen (und hier im Forum posten, wenn du Hilfe von uns willst). Ich nehme aber mal an, dass das hier die Meldung war: warning: conflicting types for built-in function 'putchar'

Die Meldung versucht dir zu sagen, dass es im C-Standard eine Funktion namens putchar gibt, die allerdings eine andere Signatur (int putchar(int)) hat. Ich empfehle dir deine Funktion anders zu nennen. Du kannst aber auch die Signatur korrigieren. Allerdings ist das immer etwas problematisch Funktionen wie Standardfunktionen zu nennen, wenn sie nicht genau das tun, was auch im Standard steht. Die dritte Möglichkeit wäre GCC zu erzählen, dass es keine builtin-Funktionen gibt, indem du ihm den Parameter -fno-builtin (oder -ffreestanding) übergibst.

Das ist übrigens auch der Grund, warum ich die Funktin kputc genannt habe, und nicht putc oder putchar. Wenn dir der Name nicht gefällt, hab ich noch cons_putc, console_putc, screen_putc, debug_putc und cga_putc im Angebot. Mit oder ohne Unterstrich, oder als console_putchar, etc. kannst du nach eigenem Geschmack anpassen.
« Letzte Änderung: 12. November 2012, 21:12 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

jo2105

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 12. November 2012, 21:19 »
Du solltest immer die gesamte Fehlermeldung lesen (und hier im Forum posten, wenn du Hilfe von uns willst). Ich nehme aber mal an, dass das hier die Meldung war: warning: conflicting types for built-in function 'putchar'

Die Meldung versucht dir zu sagen, dass es im C-Standard eine Funktion namens putchar gibt, die allerdings eine andere Signatur (int putchar(int)) hat. Ich empfehle dir deine Funktion anders zu nennen. Du kannst aber auch die Signatur korrigieren. Allerdings ist das immer etwas problematisch Funktionen wie Standardfunktionen zu nennen, wenn sie nicht genau das tun, was auch im Standard steht. Die dritte Möglichkeit wäre GCC zu erzählen, dass es keine builtin-Funktionen gibt, indem du ihm den Parameter -fno-builtin (oder -ffreestanding) übergibst.

Das ist übrigens auch der Grund, warum ich die Funktin kputc genannt habe, und nicht putc oder putchar. Wenn dir der Name nicht gefällt, hab ich noch cons_putc, console_putc, screen_putc, debug_putc und cga_putc im Angebot. Mit oder ohne Unterstrich, oder als console_putchar, ... kannst du auch nach eigenem Geschmack anpassen.

Ich wollte meinen Post gerade editieren.^^  Wie du sagtest hat es nach einer Namensänderung funktioniert. (Vielleicht sollte ich mal länger ausprobieren bevor ich poste^^)

BTW: Ist es irgendwie möglich die Makefile so zu erweitern, dass sie im Falle einer geänderten Headerdatei alles kompiliert? Es ist ziemlich nervig, dass man jede .c Datei neu speichern muss, wenn man eine Headerdatei ändert, die für alle oder mehrere .c Dateien notwendig sind.  :roll:

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 12. November 2012, 21:27 »
Ja, die Low-Tech-Variante wäre in der Makefile einfach alle Abhängigkeiten aufzulisten:

foo.o: foo.h bar.h
bar.o: bar.h

Das heißt foo.o muss neugebaut werden, wenn sich foo.h oder bar.h ändern (oder natürlich wenn sich foo.c ändert, aber die Regel ist woanders definiert).

Das kannst du dir allerdings auch vom GCC automatisch generieren lassen und in die Datei einbinden. Dazu musst du GCC den Parameter -MMD übergeben. Dann erzeugt er neben der Objektdatei noch eine Datei mit den Dependencies namens foo.d. Mal angenommen du hast eine Variable in der Makefile mit allen Objektdateien namens $(OBJS):

DEPS = $(addsuffix .d, $(basename $(OBJS)))
-include $(DEPS)

Die Variable $(DEPS) enthält dann die Liste der Dateinamen wobei die Endung durch .d ersetzt wurde, z.B. foo.d bar.d. Das -include bindet diese Dateien ein. Das - vor dem include sorgt dafür, dass Make keinen Fehler meldet, wenn die Dateien nicht existieren (z.B. weil die Quelltextdatei noch nicht kompiliert wurde oder nach einem make clean). Du solltest bei make clean auch die $(DEPS) löschen.
Dieser Text wird unter jedem Beitrag angezeigt.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 13. November 2012, 13:29 »
Vielleicht sollten wir gescheite Abhängigkeiten mal in die Tutorial-Makefile einbauen.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

jo2105

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 13. November 2012, 20:39 »
char* itoa(int data, char* buf, int base)
{
  char * result = buf;
  char * digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVW";
  int i;
 
  for(i = 0; data != 0; i++) {
    buf[i] = digits[data % base];
    data = data / base;
    //kputc(buf[i]);
  }
 
  *buf = 0;
 
  return result;
}

Soweit bin ich momentan damit. Wenn man die das kputc(buf); entkommentiert, wird die Zahl auch (eben nur falsch herum) ausgegeben. Allerdings scheint der Rückgabewert leer zu sein.

Wie würde bei dir der Funktionsaufruf dafür aussehen?

/e: Mensch^^ Immer wenn ich ein Problem habe, denke ich erstmal ne viertel Stunde drüber nach, dann poste ich es hier, guck mir den Code nochmal an und finde den Fehler. :D

In dem Fall muss man einfach das *buf = 0 herausnehmen.
« Letzte Änderung: 13. November 2012, 20:51 von jo2105 »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 13. November 2012, 21:45 »
Du solltest trotzdem den String terminieren. buf = 0; oder so.
Dieser Text wird unter jedem Beitrag angezeigt.

jo2105

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 13. November 2012, 21:50 »
Du solltest trotzdem den String terminieren. buf = 0; oder so.

Geht es nicht auch einfach, dass man buf[i+1] = '\0' schreibt? Das wäre mir zumindest zuerst in den SInn gekommen um den String zu terminieren.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 13. November 2012, 21:55 »
Oh ja sowas meinte ich. Da hat der BBCode mir den Post versaut. Ich wollte buf[i] schreiben, aber buf[i+1] könnte auch richtig sein, je nachdem welchen Wert i hat.
Dieser Text wird unter jedem Beitrag angezeigt.

jo2105

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 13. November 2012, 22:01 »
Oh ja sowas meinte ich. Da hat der BBCode mir den Post versaut. Ich wollte buf[i] schreiben, aber buf[i+1] könnte auch richtig sein, je nachdem welchen Wert i hat.

Jep. In meinem Code müsste der i-Wert nach verlassen der for-Schleife den Index von dem letzten Zeichen haben. Aber das wäre ja ein Fehler den man schnell findet.

 

Einloggen