Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: Cool-Andy am 17. October 2009, 19:43
-
Hallo nochmal,
ich hätte da nochmal ne wahrscheinlich ziemlich dumme Anfängerfrage:
wie kann man mit möglichst Standard-C-Funktionen einen char-String in eine int-Zahl umwandeln?
-
Hallo,
wenn Du ein char[] mit z.B. "123" meinst dann ist 'sscanf()' Dein Freund.
Grüße
Erik
-
Und wie implentier ich die in mein OS? :?
-
Hallo,
"Standard-C-Funktionen" sind in der Standard-C-Bibliothek. Wenn Du die nicht in Deinen Kernel mit reinlinken möchtest, was auch nicht empfehlenswert ist, dann schau Dich mal beim Linux-Kernel um die haben eine abgespeckte Fassung (sscanf unterstützt z.B. keine Gleitkommazahlen) aus der Du bestimmt alles benötigte herrausfiletieren kannst.
Grüße
Erik
-
atoi() währe wohl die direkte Funktion wenn du die hättest.
Und wie implentier ich die in mein OS? :?
Ich würde Zeichen für Zeichen einlesen, die entsprechende Ziffer finden und dann die Ziffern zusammensetzen. So nach dem Motto "1234" ist 1*1000+2*100+3*10+4*1.
-
Von sscanf würde ich abraten solange man sich solche Fragen stellt.
Einfacher ist atoi:
1. Setze Variable x auf 0
2. Zeichen einlesen
3. Zeichen von ASCII in den Wert der Stelle umwandeln
4. x <- x * 10 + Wert aus 3.
5. Wenn noch nicht am Ende -> gehe zu 2.
6. Gib x zurück.
-
strtol() ist noch besser. Muss aber auch erst implementiert werden. ;)
Der Ansatz von Tobiking dürfte dich schnell zum Ziel führen. Ein einzelnes Zeichen (von dem du weißt, dass es eine Ziffer ist) wandelst du am geschicktesten mit c - '0' in eine Zahl um.
-
Vielen Danke für die Antworten,
aber die von PorkChicken hab ich noch nich so wirklich verstanden, könntest du mal ein Stück Code posten (ich denke, Pseudocode würde reichen).
-
Das war ja im prinzip Pseudocode ;)
Aber hier mal als ungetestes C-Schnipsel, und in den Kommentaren die Bezüge zum Pseudocode:
int atoi(const char *str)
{
int x = 0; // der Wert // (1.)
int d; // eine Ziffer
while (*str) {
d = *str - '0'; // (2.) + (3.)
x = x * 10 + d; // (4.)
str++;
}
return x; // (6.)
}
Der Punkt (5.) ist schwer in diesem Code unterzubringen, aber entspricht wohl am ehesten der while-Schleife.
Da fehlt jetzt natürlich die Verarbeitung von negativen Zahlen ;)
-
OK,
vielen Dank. Könntest du mir auch noch sagen, wie man wieder von int nach char umwandelt. (Bitte nicht wundern, hab bis jetzt noch nie ints und chars umwandeln müssen.)
-
Du teilst die Zahl immer wieder durch 10, der Rest gibt jeweils genau die letzte Ziffer. Folgender Pseudocode gibt die Zahl also rückwärts aus, das Umdrehen überlasse ich dir. ;)
while n > 0 do
putc('0' + (n % 10));
n := n / 10;
od
-
OK, vielen Dank, werd ich gleich mal ausprobieren.
-
Ich cheks net, entweder bei mir kommt nur Müll raus (Zeichen wie sowas: █), oder es kommt gar nix.
Könntest du bitte mal einen C-Code posten?
Ich denke, es wäre eine große Hilfe für mich. :mrgreen:
-
Hallo,
int itostr(char* const str, int zahl)
{
if (zahl == 0)
{ *str = '0'; return 1; } //bei 0 nur "0" zurückgeben
int i = 0; //String-Länge
while (zahl > 0) { //Loop solange bis Zahl 0 ist
const int rest = zahl % 10;
zahl = zahl / 10;
str[i++] = ((char)rest) + '0';
}
return i; //Länge des Strings zurückgeben
}
Da fehlt auch wieder die Behandlung negativer Zahlen aber ansonsten sollte das funktionieren.
Grüße
Erik
-
Ok,
hab die Funktion übernommen, aber irgendwie funzts net.
Ich poste mal meinen Code:
string.c:
// ...
int atoi(const char *str)
{
int x = 0; // der Wert // (1.)
int d; // eine Ziffer
while (*str) {
d = *str - '0'; // (2.) + (3.)
x = x * 10 + d; // (4.)
str++;
}
return x; // (6.)
}
int itoa(int zahl)
{
if (zahl == 0)
{
key[0] = '0';
key[1] = '\0';
return 1;
} //bei 0 nur "0" zurückgeben
int i = 0; //String-Länge
while (zahl > 0) { //Loop solange bis Zahl 0 ist
const int rest = zahl % 10;
zahl = zahl / 10;
key[i++] = ((char)rest) + '0';
}
key[i] = '\0';
return i; //Länge des Strings zurückgeben
}
und main.c:
// ...
i = 0;
printf ("\n");
printf ("1: ");
scanf ();
i = atoi(key);
itoa(i);
printf (key);
printf ("\n");
// ...
Wobei "key" eine 80-Zeichen lange char-Variable ist, und für den Austausch von Zeichen gedacht ist (und auch Zeichen von der Tastatur entgegen nimmt).
Erkennt ihr den Fehler? Ich bin echt ratlos! :?
-
Hallo,
aber irgendwie funzts net
Kannst Du das etwas konkreter ausdrücken?
Baue den Code einfach mal in ein normales Programm auf deinem Entwicklungsrechner ein und nimm einen Debugger, vielleicht findest Du dann den Fehler.
Grüße
Erik
-
Kannst Du das etwas konkreter ausdrücken?
Wenn eine Zahl unterhalb 4 eingegeben wird, erscheint überhaupt keine Ausgabe. Bei jeder anderen Zahl erscheint eine wirre Zahlenfolge.
Baue den Code einfach mal in ein normales Programm auf deinem Entwicklungsrechner ein und nimm einen Debugger, vielleicht findest Du dann den Fehler.
Ok, hab ich gemacht. Ich hab die Funktionen in ein Windows-Programm gepackt und siehe da funktionierts, was mich eigentlich noch ratloser macht!
-
Hallo,
Wenn eine Zahl unterhalb 4 eingegeben wird, erscheint überhaupt keine Ausgabe. Bei jeder anderen Zahl erscheint eine wirre Zahlenfolge.
Kann es vielleicht sein das etwas mit der Parameterübergabe beim Funktionsaufruf nicht passt? Ist nur so ne Idee.
... was mich eigentlich noch ratloser macht!
Das ist sehr merkwürdig. Ich schätze da wirst Du nicht drumrum kommen das ganze in kleine Häppchen zu zerlegen und alles einzeln testen bis Du die schuldige Code-Stelle gefunden hast. Hast Du schon mal den Debugger von Bochs probiert? Als erstes solltest Du IMHO probieren das Int-2-String klappt, dafür nimmst Du am besten hartcodierte Int-Werte.
Probiere mal d = *str - '0';
gegend = ((int)(*strr)) & 0x0F;
undkey[i++] = ((char)rest) + '0';
gegenkey[i++] = (char)(rest - 0x30);
zu ersetzen, eventuell hast Du ein Problem mit dem Zeichensatz.
Ansonsten solltest Du eine Fehlermeldung einbauen wenn 'rest' nicht im Wertebereich 0...9 ist. So in der Art :if ( (rest < 0) || (rest > 9) ) { printf("Error!!"); }
Viel mehr fällt mir momentan auch nicht ein.
Viel Spaß :wink:
Erik
-
Hmm, ich find den Fehler nicht! :-(
Ich hab mich entschieden, die Funktion aufzuschieben, um an meinem OS weiterzukommen, vielleicht findet ja einer von euch noch den Fehler.
Vielen Dank jeden Falls.
-
Naja an so etwas sollte man eigentlich nicht verzweifeln (müssen).
Zuerst den kompletten Code den du für diese Funktion geschrieben hast wegwerfen, damit du nicht beeinflusst wirst. Du fängst jetzt ganz von vorne an.
Guck mal. Nehmen wir mal die Zahl 12345.
durch 10 teilen:
1234 R 5
durch 10 teilen
123 R 4
durch 10 teilen
12 R 3
durch 10 teilen
1 R 2
durch 10 teilen
0 R 1
*) R = Rest
Wie du siehst ergeben die unterstrichenen Zahlen von unten nach oben gelesen deine ursprüngliche Zahl.
Was haben wir also gemacht?
1) Wir haben die Zahl genommen die wir umwandeln wollten und solange durch 10 geteilt bis das Ergebnis 0 war.
2) Wir haben jeweils den Rest jeder Division genommen und ihn in einen Char umgewandelt.
Wie macht man das? Nunja..
Die "0" hat den Ascii-Code 0x30
die "1" 0x31, die "2" 0x32 usw.
Wir können also einfach 0x30+R rechnen und haben dann den Ascii-Code der aktuellen Ziffer (R = der Rest der aktuellen Division)
den AsciiCode in einen Char umwandeln kannst du durch
(char)code
also in unserem Fall:
(char)(0x30+R)
3) Von hinten nach vorne gelesen ergeben diese Chars dann die gewünschte Zahl als String.
Wenn du dir das mal so durchgehst und evtl auch mal auf nem Blatt ausprobierst, dann schreibt sich der Code fast von alleine ;)
Lg
Cjreek
-
Danke, dass du das Thema ausgegraben hast.
Könntest du bitte den Code posten, wie du es dir gedacht hast?
Weil, ich versuch es grad mit einer while-Schleife
...
while (zahl != 0)
{
...
und des funzt net.
Wäre wirklich sehr nett von dir!
:-)
-
Wenn genau derselbe Code unter Linux/Windows funktioniert und in deinem Kernel nicht, würde ich nicht an der Funktion zweifeln, sondern am Code drum herum, zum Beispiel der Ausgabe. Wenn du überhaupt nicht weiterkommst, lad halt nochmal irgendwo deinen kompletten Code hoch, damit es jemand selber ausprobieren kann.
-
Wenn ich unter Windows debugge bricht die Anwendung in der Umwandlung von int nach char ab!
-
OK, meine "int to char"-Funktion:
void itoc (int value)
{
int rest = 0;
char letter[1];
char string[100];
int i = 0;
int z;
while (value != 0)
{
rest = value%10;
letter[0] = (char) (0x30+rest);
string[i] = letter[0];
z = value/10;
i++;
}
i++;
string[i] = '\0';
printf ("x: ");
printf (string);
}
Wenn ich diese Funktion starte, bleibt der Bildschirm schwarz! :?
EDIT:
Ich hab mein Fehler schon selber gefunden. Hier mal mein geupdateter Code:
void itoc (int value)
{
int rest = 0;
char letter[1];
char string[100];
int i = 0;
int z;
while (value != 0)
{
rest = value%10;
letter[0] = (char) (0x30+rest);
string[i] = letter[0];
z = value/10;
value = z;
i++;
}
i++;
string[i] = '\0';
printf ("x: ");
printf (string);
}
Hat jemand jetzt noch ne Idee, wie ich den String umdrehen kann? :-D
-
Hallo,
void rotate_str(char* const text,const int length)
{
//bei Länge 1 oder 0 nichts machen :
if (length < 2) { return; }
//Indices um von vorne und hinten bis zur Mitte zu arbeiten :
int low = 0;
int high = length - 1;
while (low < high)
{
//Zeichen vertauschen :
char ctemp = text[low]; text[low] = text[high]; text[high] = ctemp;
//Indices weiterschieben :
low++; high--;
}
}
'length' natürlich ohne das 0-Byte am Text-Ende, wie immer alles ohne Gewähr :wink:
Sollte aber klappen.
Grüße
Erik
-
void reverse_string(char* s)
{
char* p;
for (p = s + strlen(s) - 1; *s && p > s; *p ^= *s, *s ^= *p, *p-- ^= *s++);
}
Tut mir leid, ich konnte nicht anders. :-D
-
Tut mir leid, ich konnte nicht anders. :-D
Was auch immer mir damit sagen wolltest, die Funktion von erik.vikinger hat funktioniert.
Vielen Dank! :-D
-
Meine funktioniert auch, aber ich würde sie aus dem offensichtlichen Grund der Nichtoffensichtlichkeit nirgends so einbauen. ;)
-
Aja, OK! :mrgreen:
-
Hallo,
Tut mir leid, ich konnte nicht anders. :-D
Dieses Gefühl kenne ich nur zu gut. Aber in diesem konkreten Fall hättest Du der Versuchung ruhig widerstehen dürfen. Sowas würde ich ja schon fast in die Kategorie "Human-Code-Obfuscator" einsortieren.
In meiner Variante würde ich noch zwei Korrekturen vornehmen:
//bei Null-Pointer oder Länge 1 oder 0 nichts machen :
if ( (text == NULL) || (length < 2) ) { return; }
//Zeichen vertauschen :
const char ctemp = text[low]; text[low] = text[high]; text[high] = ctemp;
Die erste Korrektur ist sicher offensichtlich (hat taljeth in seiner Variante auch drin) und die zweite sagt das 'ctemp' nicht verändert werden darf (alternativ kann man auch 'register' anstatt 'const' nehmen um etwas mehr Performance zu bekommen, ein guter Compiler sollte das aber selber sehen).
Achja, generell 'unsigned int' anstatt 'int' währe dem Problem angemessener da Strings üblicherweise keine negative Länge haben.
EDIT: den Funktions-Namen von taljeth finde ich besser.
Grüße
Erik
-
Die erste Korrektur ist sicher offensichtlich (hat taljeth in seiner Variante auch drin)
Nein, wenn sie NULL kriegt, segfaultet meine Funktion, ganz im Sinne der klassischen C-Stringfunktionen. Schon bei der Initialisierung von p (im strlen), aber auch die for-Bedingung wäre dafür falschrum geschrieben.
-
Hallo,
aber auch die for-Bedingung wäre dafür falschrum geschrieben.
Stimmt, jetzt hab ichs gesehen.; *s && p > s;
Mit dem '*s' prüfst Du ja das s nicht auf ein 0-Byte zeigt (und nicht das s kein Null-Pointer ist), was eigentlich nicht passieren dürfte da s ja nur bis zur Hälfte durch den String wandert und bei einem leeren String sollte 'p > s' für einen Schleifen-Abbruch sorgen.
ganz im Sinne der klassischen C-Stringfunktionen
Aha, wusste ich nicht. Wenn eine C++ Library eine Exception wirft ist das ja in Ordnung, die Exception kann man fangen oder eben nicht, aber den ganzen Prozess vom OS killen zu lassen find ich nicht gut und ein Signal-Handler im User-Programm kann IMHO nicht so viel ausrichten da er kaum rauskriegt wo im Code das Problem entstand und erst recht nicht das Programm sinnvoll weiterlaufen lassen kann.
wenn sie NULL kriegt, segfaultet meine Funktion
Woher kommt eigentlich "segfault"? Gab es mal ein UNIX das Segmentierung unterstützt hat? Da ja in Wirklichkeit ein Pagefault (die 0te Page lässt man ja extra dafür immer ungemappt) dahinter steckt find ich den Namen irgendwie unpassend.
@Cool-Andy : sorry für meine OT-Frage aber ich gehe davon aus das Dein eigentliches Problem von diesem Thread gelöst ist.
Grüße
Erik
-
Mit dem '*s' prüfst Du ja das s nicht auf ein 0-Byte zeigt (und nicht das s kein Null-Pointer ist), was eigentlich nicht passieren dürfte da s ja nur bis zur Hälfte durch den String wandert und bei einem leeren String sollte 'p > s' für einen Schleifen-Abbruch sorgen.
Stimmt, das ist Blödsinn. Gedacht war es für den leeren String.
Aha, wusste ich nicht. Wenn eine C++ Library eine Exception wirft ist das ja in Ordnung, die Exception kann man fangen oder eben nicht, aber den ganzen Prozess vom OS killen zu lassen find ich nicht gut und ein Signal-Handler im User-Programm kann IMHO nicht so viel ausrichten da er kaum rauskriegt wo im Code das Problem entstand und erst recht nicht das Programm sinnvoll weiterlaufen lassen kann.
Man übergibt den Stringfunktionen eben keine Nullpointer. ;)
Ich hab jetzt keine großartige Lust, damit rumzuspielen, aber theoretisch müsste der Signalhandler die Codestelle schon rauskriegen, das sollte ja seine eigene Rücksprungadresse sein. Ob man damit was sinnvolles anstellen kann, ist die andere Frage. Wahrscheinlich könnte man mit setjmp/longjmp eher ein Exceptionhandling aufbauen und bräuchte dann die Adresse vom Fault überhaupt nicht.
Woher kommt eigentlich "segfault"? Gab es mal ein UNIX das Segmentierung unterstützt hat? Da ja in Wirklichkeit ein Pagefault (die 0te Page lässt man ja extra dafür immer ungemappt) dahinter steckt find ich den Namen irgendwie unpassend.
Wird wohl so sein, aber ich kann dir nicht sagen, welche Unixe das waren.
Wikipedia sagt: "Die englische Bezeichnung segmentation fault stammt aus einer Zeit, in der Speicherbereiche durch Segmentierung vor unerlaubtem Zugriff geschützt wurden. Heute werden andere Schutzmechanismen verwendet, insbesondere Paging; der Terminus segmentation fault hat sich aber unverändert erhalten."
-
Hallo,
Man übergibt den Stringfunktionen eben keine Nullpointer. :wink:
Ah ja, das ist natürlich richtig. Trotzdem könnte z.B. strlen() -1 zurückliefern, würde ich zumindest als nett einstufen.
Meine Frage ziehlte aber eher darauf ab ob das so definiert ist das die Standard-Funktionen eben nicht selber auf einen NULL-Pointer prüfen sollen. Es könnte ja auch eine Plattform geben wo NULL-Pointer nicht zu einer (CPU-)Exception führen.
.... Ob man damit was sinnvolles anstellen kann, ist die andere Frage.
Es währe auf jeden Fall mit erheblicher Arbeit für den Programmierer verbunden, dann sollte man doch lieber zu C++ wechseln und diese Arbeit dem Compiler überlassen.
".... der Terminus segmentation fault hat sich aber unverändert erhalten."
Na dann wird es ja Zeit das er auf meiner Plattform wieder zu altem Ruhm aufsteigt. :wink:
Jedenfalls hab ich extra dafür jedem Segment nicht nur ein Limit sondern auch ein Minimum angedacht, somit ist das Offset 0 (falls das OS die Segmente so einrichtet) nicht nutzbar und löst einen segfault aus. Den Selector 0 lehnt die CPU ebenfalls ab.
Das Minimum möchte ich nun aber auch benutzen um nicht Speicher am Anfang der Segmente zu verschwenden falls ich mal ASLR implementieren sollte.
Grüße
Erik
-
Ah ja, das ist natürlich richtig. Trotzdem könnte z.B. strlen() -1 zurückliefern, würde ich zumindest als nett einstufen.
Um ein bisschen Erbsen zu zählen: strlen gibt einen size_t zurück, also kann es kein -1 liefern. Aber ja, natürlich hätte man das so definieren können, dass es irgendeine Form von Fehlerbehandlung macht (allerdings bringt der Test auf NULL auch nicht so viel, im Zweifelsfall steckt das Ding in einer struct und hat ein Offset von NULL) - hat man aber eben nicht.
Meine Frage ziehlte aber eher darauf ab ob das so definiert ist das die Standard-Funktionen eben nicht selber auf einen NULL-Pointer prüfen sollen. Es könnte ja auch eine Plattform geben wo NULL-Pointer nicht zu einer (CPU-)Exception führen.
Es ist nicht so definiert, dass sie es nicht prüfen dürfen oder dass sie eine Exception werfen sollen. Für strlen selber ist überhaupt kein Fehlerfall definiert, das Dereferenzieren eines Nullpointers ist explizit undefiniert. Mit strlen(NULL) kann das jeweilige System also machen, was ihm gerade in den Kram passt.