2009-08-04 12 views
2

ich etwa eine halbe Stunde vor kurzem verschwendet dieses seltsame Verhalten in NSLog Aufspüren von (...):NSLog (...) falscher Formatbezeichner beeinflusst andere Variablen?

NSString *text = @"abc"; 
long long num = 123; 
NSLog(@"num=%lld, text=%@",num,text); //(A) 
NSLog(@"num=%d, text=%@",num,text); //(B) 

Line (A) druckt die erwartete "num = 123, text = abc", aber Linie (B) druckt "num = 123, text = (null)". Offensichtlich

, Drucken ein long long mit %d ein Fehler, aber kann jemand erklären, warum es verursachen würde text als null gedruckt werden?

+0

Wenn Sie mit der Option -Wall kompilieren, warnt der Compiler Sie vor Problemen wie diesem; Ich empfehle auch dringend den -Werror so Warnungen immer den Build zu brechen. –

+1

@Adam Rosenfield, nur ein Hinweis, dass die Unterstützung für die Formatprüfung, ala '-Wformat', in gcc/objc immer ein bisschen zweifelhaft war. Dies scheint mit späteren Versionen des Compilers besser zu werden, aber ich habe nur eine kurze Überprüfung unter Xcode 3.1 durchgeführt und es ist der obige Fehler nicht aufgetreten. – johne

+0

Es fängt den Fehler nicht ab, da -Wformat nur mit C-Zeichenfolgen funktioniert (wie in printf) und vollständig NSString * -Objektkonstanten (die NSLog verwendet) nicht analysieren kann. –

Antwort

9

Sie haben gerade die Speicherausrichtung auf Ihrem Stack durcheinander gebracht. Ich nehme an, als Sie das neueste Apple-Produkt mit x86-Prozessor verwenden. Unter Berücksichtigung dieser Annahmen sieht Ihr Stack in beiden Fällen so aus:

 
    |  stack   | first | second | 
    +---------------------+-------+--------+ 
    |  123   |  | %d | 
    +---------------------+ %lld +--------+ 
    |   0   |  | %@ | 
    +---------------------+-------+--------+ 
    | pointer to text | %@ |ignored | 
    +---------------------+-------+--------+ 

In der ersten Situation legen Sie Stack 8 Bytes und dann 4 Bytes. Und dann wird NSLog angewiesen, vom Stapel 12 Bytes zurückzunehmen (8 Bytes für %lld und 4 Bytes für %@).

In der zweiten Situation weisen Sie NSLog an, zuerst 4 Bytes zu nehmen (%d). Da Ihre Variable 8 Bytes lang ist und eine wirklich kleine Zahl enthält, sind ihre oberen 4 Bytes 0. Wenn NSLog dann versucht, Text zu drucken, wird nil vom Stapel genommen.

Da das Senden der Nachricht an nil in Obj-C gültig ist NSLog wird nur senden description: zu nil bekommen wahrscheinlich nichts und dann drucken (null).

Am Ende seit Objective-C ist nur C mit Ergänzungen, räumt Anrufer ganze Unordnung.

1

Wie varargs implementiert werden, ist systemabhängig. Was jedoch wahrscheinlich passiert, ist, dass die Argumente nacheinander in einem Puffer gespeichert werden, obwohl die Argumente unterschiedliche Größen haben können. Also die ersten 8 Bytes (vorausgesetzt, das ist die Größe eines long long int) der Argumente ist die long long int, und die nächsten 4 Bytes (vorausgesetzt, dass die Größe eines Zeigers auf Ihrem System ist) ist der NSString Zeiger.

Dann, wenn Sie die Funktion sagen, dass es eine int und dann einen Zeiger erwartet, erwarten, dass es die ersten 4 Bytes der int sein (unter der Annahme, das ist die Größe eines int) und die nächsten 4 Bytes der Zeiger zu sein. Wegen der besonderen Endianität und Anordnung der Argumente auf Ihrem System sind die ersten 4 Bytes des long long int zufällig die niedrigstwertigen Bytes Ihrer Zahl, also druckt es 123. Dann liest es für den Objektzeiger die nächsten 4 Bytes, die In diesem Fall sind die höchstwertigen Bytes Ihrer Nummer, die alle 0 sind, so dass dies als nil Zeiger interpretiert wird. Der tatsächliche Zeiger wird nie gelesen.