2009-06-22 4 views
1

Ich habe eine große Zahl in einer Zeichenfolge gespeichert und versuche, eine einzelne Ziffer zu extrahieren. Aber was sind die Unterschiede zwischen diesen Anrufen?Was ist der Unterschied zwischen meinen atoi() - Rufen?

#include <iostream> 
#include <string> 

int main(){ 
    std::string bigNumber = "93485720394857230"; 
    char tmp = bigNumber.at(5); 
    int digit = atoi(&tmp); 
    int digit2 = atoi(&bigNumber.at(5)) 
    int digit3 = atoi(&bigNumber.at(12)); 
    std::cout << "digit: " << digit << std::endl; 
    std::cout << "digit2: " << digit2 << std::endl; 
    std::cout << "digit3: " << digit3 << std::endl; 
} 

Dies wird die folgende Ausgabe erzeugen.

Zahl: 7

digit2: 2147483647

digit3: 57230

Die erste ist das gewünschte Ergebnis. Der zweite scheint mir eine Zufallszahl zu sein, die ich in der Zeichenfolge nicht finden kann. Die dritte ist das Ende der Saite, aber nicht nur eine einzelne Ziffer, wie ich es erwartet hatte, sondern vom 12. bis zum Ende der Saite. Kann mir jemand die verschiedenen Ergebnisse erklären?

EDIT: Wäre das eine akzeptable Lösung?

char tmp[2] = {bigNumber.at(5), '\0'}; 
int digit = atoi(tmp); 
std::cout << "digit: " << digit << std::endl; 
+0

Das scheint wie Hausaufgaben. – Eric

+0

Nun, ich habe tatsächlich versucht, das Problem 8 des Euler-Projekts zu lösen. Ich bin schon für dieses Semester fertig :-) – Lucas

Antwort

5

Es ist alles mehr oder weniger erklärbar.

int main(){ 
    std::string bigNumber = "93485720394857230"; 

Diese Zeile kopiert das einzelne Zeichen '5' in die Zeichenvariable. atoi wird dies korrekt konvertieren. atoi erwartet, dass der Zeichenfolgenparameter eine gültige 0-terminierte Zeichenfolge ist. &tmp ist nur ein Zeiger auf die Zeichenvariable - das Verhalten dieses Aufrufs ist nicht definiert, da der Speicher, der dem Zeichen im Speicher unmittelbar folgt, unbekannt ist. Um genau zu sein, müssten Sie eine nullterminierte Zeichenfolge erstellen und diese übergeben.*

char tmp = bigNumber.at(5); 
    int digit = atoi(&tmp); 

Diese Zeile erhält einen Zeiger auf das Zeichen in Position 5 in der Zeichenfolge. Dies ist ein Zeiger auf die ursprüngliche große Zahl oben - also der String-Parameter atoi sieht aus wie die Zeichenfolge "5720394857230". atoi wird klar overflow versuchen, dies in eine ganze Zahl zu verwandeln, da keine 32-Bit-Integer dies halten wird.

int digit2 = atoi(&bigNumber.at(5)) 

Diese Linie erhält einen Zeiger in den String an Position 12. Der Parameter atoi Der String ist "57230". Dies wird korrekt in die Ganzzahl 57230 konvertiert.

int digit3 = atoi(&bigNumber.at(12)); 

... }

Da Sie C++ verwenden, gibt es schönere Methoden Zeichenkette in Zahlen zu konvertieren. Eine davon ist die Boost lexical_cast-Bibliothek. Sie würde es so verwenden:

char tmp = bigNumber.at(5); 
// convert the character to a string then to an integer 
int digit = boost::lexical_cast<int>(std::string(tmp)); 

// this copies the whole target string at position 5 and then attempts conversion 
// if the conversion fails, then a bad_lexical_cast is thrown 
int digit2=boost::lexical_cast<int>(std::string(bigNumber.at(5))); 

* Streng genommen atoi wird die numerischen Zeichen scannen durch, bis ein nicht-numerische eines gefunden wird. Es ist eindeutig undefiniert, wann es einen finden wird und was es tun wird, wenn ungültige Speicherplätze gelesen werden.

+0

Vielen Dank für diese ausgefallene Lösung. Verwenden Sie lexical_cast sicherer als atoi() oder warum schlagen Sie vor, es zu verwenden? – Lucas

+0

lexical_cast ist viel sicherer, weil es moderne C++ - Idiome nutzt, um Typensicherheit zu geben. Sie geben die Ziel- und Quelltypen an (die Quelle wird im obigen Code abgeleitet) - sie prüft auf Fehler auf dem Weg und löst Ausnahmen aus, falls die Konvertierung nicht durchgeführt werden konnte. atoi ist eine API älteren Stils aus der C-Standardbibliothek. lexical_cast hat zusätzliche Vorteile, da es mit viel mehr definierten Typen arbeiten kann (einschließlich Ihrer eigenen definierten Typen und Klassen) –

+0

Vielen Dank für die Klarstellung. Ich denke, Sie müssen bei der Größe der Zeichenfolge in Ihrem Zeichenfolgenkonstruktor, also "string (size_t n, char c);" kann angerufen werden. Ich benutze jetzt: int digit = boost :: lexical_cast (std :: string (1, bigNumber.at (5))); – Lucas

3

bigNumber.at() keine neue Zeichenfolge mit einem einzelnen Zeichen zurück, aber die Adresse eines Zeichens in der Zeichenkette. Der zweite Anruf ist also tatsächlich:

atoi("720394857230") 

, wodurch der interne Algorithmus überläuft. Der erste Anruf ist sehr gefährlich, da er vom (zufälligen) Wert im Speicher (&tmp)+1 abhängt.

Sie haben einen String mit zwei Zeichen zuzuweisen, weisen die einzelne Zeichen aus bigNumber.at() zum ersten und \0 an den zweiten und dann atoi() mit der Adresse des temporären String nennen.

+0

Danke für die schnelle Antwort. Heißt das, dass meine erste Lösung nur funktioniert, weil ich zufällig nach "& tmp" "\ 0" in meinem Gedächtnis habe? – Lucas

+3

atoi() stoppt bei jedem nicht-numerischen Zeichen, so dass Sie eine Chance von 96% (245 bis 10) haben, dass es funktioniert. –

5

Ich weiß, warum die 2. Nummer angezeigt wird.

From the atoi reference.

Wenn der richtige Wert aus dem Bereich der darstellbaren Werte ist, INT_MAX oder INT_MIN zurückgegeben.

2147483647 ist INT_MAX

3

Das Argument atoi sollte eine Null-terminierten String sein.

3

Funktion at gibt Zeiger auf Zeichen in der Zeichenfolge. Funktion atoi konvertiert Zeichenfolge in int, nicht nur ein Zeichen.

Verwandte Themen