NSLog / printf-Spezifizierer für NSInteger?

131

A NSIntegerist 32 Bit auf 32-Bit-Plattformen und 64 Bit auf 64-Bit-Plattformen. Gibt es einen NSLogBezeichner, der immer der Größe von entspricht NSInteger?

Konfiguration

  • Xcode 3.2.5
  • llvm 1.6 Compiler (das ist wichtig; gcc macht das nicht)
  • GCC_WARN_TYPECHECK_CALLS_TO_PRINTF eingeschaltet

Das macht mir hier Sorgen:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    @autoreleasepool {
        NSInteger i = 0;
        NSLog(@"%d", i);
    }
    return 0;
}

Für 32-Bit-Code benötige ich den %dBezeichner. Wenn ich jedoch den %dBezeichner verwende, wird beim Kompilieren für 64-Bit eine Warnung angezeigt, die darauf hinweist, dass ich %ldstattdessen verwende.

Wenn ich %lddie 64-Bit-Größe anpasse, wird beim Kompilieren für 32-Bit-Code eine Warnung angezeigt, die darauf hinweist, dass ich %dstattdessen verwende.

Wie behebe ich beide Warnungen gleichzeitig? Gibt es einen Bezeichner, mit dem ich arbeiten kann?

Dies wirkt sich auch auf [NSString stringWithFormat:]und aus [[NSString alloc] initWithFormat:].

Steven Fisher
quelle

Antworten:

296

Aktualisierte Antwort:

Sie können von dem machen zund tModifikatoren Griff NSIntegerund NSUIntegerohne Warnungen auf allen Architekturen.

Sie möchten %zdfür signiert, %tufür nicht signiert und %txfür hex verwenden.

Diese Informationen wurden freundlicherweise von Greg Parker zur Verfügung gestellt .


Ursprüngliche Antwort:

Der offiziell empfohlene Ansatz besteht darin, %ldals Spezifizierer zu verwenden und das eigentliche Argument in a umzuwandeln long.

Lily Ballard
quelle
6
Dies ist definitiv der richtige Weg, aber ich denke, ich könnte es nutzen static inline NSIntToLong(NSInteger i) {return (long)i;}. Dadurch wird vermieden, dass die Typprüfung vollständig deaktiviert wird (dh wenn sich der Typ von i ändert).
Steven Fisher
3
Gutes Denken von @ steven-Fisher. Vermeiden Sie Warnungen mit:static inline long NSIntToLong(NSInteger i) {return (long)i;}
Erik
3
Sie können auch eine NS-Nummer erstellen und diese protokollieren. NSLog(@"%@",@(mynsint)); stackoverflow.com/questions/20355439/…
orkoden
2
@ KevinBallard Dies sollte kein ernstes Leistungsproblem sein. Sie sollten ohnehin nicht viel NSLog im Produktionscode verwenden. Wenn Sie aus irgendeinem Grund viele Dinge protokollieren müssen, tun Sie dies in einem separaten Thread.
Orkoden
4
Ab Xcode 9.3 gibt es eine Warnung, wenn NSInteger als Formatargument verwendet wird mit %zd:Values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead
Rob MacEachern
2

Die akzeptierte Antwort ist absolut vernünftig, standardkonform und korrekt. Das einzige Problem ist, dass es nicht mehr funktioniert, was völlig Apples Schuld ist.

Das Format% zd ist das C / C ++ - Standardformat für size_t und ssize_t. Wie NSInteger und NSUInteger sind size_t und ssize_t auf einem 32-Bit-System 32-Bit und auf einem 64-Bit-System 64-Bit. Und deshalb hat das Drucken von NSInteger und NSUInteger mit% zd funktioniert.

NSInteger und NSUInteger sind jedoch auf einem 64-Bit-System als "long" und auf einem 32-Bit-System als "int" definiert (64 gegenüber 32 Bit). Heute wird size_t definiert auf „lang“ auf allen Systemen, das ist die gleiche Größe wie NSInteger (entweder 64 oder 32 Bit), sondern ein anderer Typ. Entweder haben sich die Warnungen von Apple geändert (sodass der falsche Typ nicht an printf übergeben werden kann, obwohl die richtige Anzahl von Bits vorhanden ist), oder die zugrunde liegenden Typen für size_t und ssize_t haben sich geändert. Ich weiß nicht welche, aber% zd hat vor einiger Zeit aufgehört zu arbeiten. Es gibt heute kein Format, das NSInteger ohne Warnung auf 32- und 64-Bit-Systemen druckt.

Das Einzige, was Sie leider tun können: Verwenden Sie% ld und wandeln Sie Ihre Werte von NSInteger in long oder von NSUInteger in unsigned long um.

Sobald Sie nicht mehr für 32 Bit bauen, können Sie einfach% ld ohne Cast verwenden.

gnasher729
quelle
0

Die Formatierer stammen aus der Standardfunktion UNIX / POSIX printf. Verwenden Sie % lu für unsigned long ,% ld für long,% lld für long long und % llu für unsigned long long . Versuchen Sie es mit man printf auf der Konsole, aber auf dem Mac ist es unvollständig. Die Linux-Manpages sind expliziter http://www.manpages.info/linux/sprintf.3.html

Beide Warnungen können nur von NSLog behoben werden (@ "% lu", (unsigned long) arg); kombiniert mit einer Besetzung, da der Code in 32 UND 64 Bit für iOS kompiliert wird. Andernfalls erstellt jede Zusammenstellung eine separate Warnung.

Katze
quelle