NSLog / printf specifier สำหรับ NSInteger?


131

A NSIntegerคือ 32 บิตบนแพลตฟอร์ม 32 บิตและ 64 บิตบนแพลตฟอร์ม 64 บิต มีตัวNSLogระบุที่ตรงกับขนาดNSIntegerหรือไม่

ติดตั้ง

  • Xcode 3.2.5
  • llvm 1.6 คอมไพเลอร์(นี่เป็นสิ่งสำคัญ gcc ไม่ทำสิ่งนี้)
  • GCC_WARN_TYPECHECK_CALLS_TO_PRINTF เปิด

นั่นทำให้ฉันเสียใจที่นี่:

#import <Foundation/Foundation.h>

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

สำหรับรหัส 32 บิตฉันต้องการตัว%dระบุ แต่ถ้าฉันใช้ตัว%dระบุฉันจะได้รับคำเตือนเมื่อคอมไพล์เป็น 64 บิตแนะนำให้ใช้%ldแทน

หากฉันใช้%ldเพื่อให้ตรงกับขนาด 64 บิตเมื่อรวบรวมรหัส 32 บิตฉันได้รับคำเตือนว่าให้ใช้%dแทน

ฉันจะแก้ไขคำเตือนทั้งสองพร้อมกันได้อย่างไร มีตัวระบุที่ฉันสามารถใช้ได้หรือไม่?

นอกจากนี้ยังส่งผลกระทบและ[NSString stringWithFormat:][[NSString alloc] initWithFormat:]

คำตอบ:


296

คำตอบที่อัปเดต:

คุณสามารถใช้ประโยชน์จากzและโมดิtฟายเออร์เพื่อจัดการNSIntegerและNSUIntegerไม่มีคำเตือนในทุกสถาปัตยกรรม

คุณต้องการใช้%zdสำหรับเซ็นชื่อ%tuสำหรับไม่ได้ลงนามและ%txสำหรับเลขฐานสิบหก

ข้อมูลนี้มามารยาทของเกร็กปาร์กเกอร์


คำตอบเดิม:

วิธีการแนะนำอย่างเป็นทางการคือการใช้เป็นตัวระบุของคุณและจะโยนข้อโต้แย้งที่เกิดขึ้นจริงกับ%ldlong


6
นี้แน่นอนวิธีที่จะไป static inline NSIntToLong(NSInteger i) {return (long)i;}แต่ผมคิดว่าผมอาจจะใช้ วิธีนี้จะหลีกเลี่ยงการปิดใช้งานการตรวจสอบประเภทโดยสิ้นเชิง (เช่นหากประเภทของ i เปลี่ยนไป)
Steven Fisher

3
คิดดีโดย @ steven-fisher. หลีกเลี่ยงการเตือนด้วย:static inline long NSIntToLong(NSInteger i) {return (long)i;}
เอริก

3
คุณยังสามารถสร้าง NSNumber และบันทึกข้อมูลนั้นได้ NSLog(@"%@",@(mynsint)); stackoverflow.com/questions/20355439/…
orkoden

2
@KevinBallard นี่ไม่ควรเป็นปัญหาด้านประสิทธิภาพที่ร้ายแรง คุณไม่ควรใช้ NSLog จำนวนมากในรหัสการผลิตอยู่ดี หากคุณต้องบันทึกข้อมูลจำนวนมากด้วยเหตุผลบางประการให้ทำในเธรดแยกต่างหาก
orkoden

4
ณ Xcode 9.3 มีคำเตือนเมื่อใช้ NSInteger เป็นอาร์กิวเมนต์รูปแบบด้วย%zd:Values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead
Rob MacEachern

2

คำตอบที่ได้รับการยอมรับมีความสมเหตุสมผลเป็นไปตามมาตรฐานและถูกต้อง ปัญหาเดียวคือมันไม่ทำงานอีกต่อไปซึ่งเป็นความผิดของ Apple โดยสิ้นเชิง

รูปแบบ% zd เป็นรูปแบบมาตรฐาน C / C ++ สำหรับ size_t และ ssize_t เช่นเดียวกับ NSInteger และ NSUInteger size_t และ ssize_t คือ 32 บิตบนระบบ 32 บิตและ 64 บิตในระบบ 64 บิต และนั่นคือเหตุผลที่การพิมพ์ NSInteger และ NSUInteger โดยใช้% zd ทำงานได้

อย่างไรก็ตาม NSInteger และ NSUInteger ถูกกำหนดให้เป็น "long" บนระบบ 64 บิตและเป็น "int" บนระบบ 32 บิต (ซึ่งก็คือ 64 vs 32 บิต) ทุกวันนี้ size_t ถูกกำหนดเป็น "long" ในทุกระบบซึ่งมีขนาดเท่ากับ NSInteger (64 หรือ 32 บิต) แต่เป็นประเภทที่แตกต่างกัน คำเตือนของ Apple มีการเปลี่ยนแปลงอย่างใดอย่างหนึ่ง (ดังนั้นจึงไม่อนุญาตให้ส่งผิดประเภทไปยัง printf แม้ว่าจะมีจำนวนบิตที่ถูกต้องก็ตาม) หรือประเภทพื้นฐานสำหรับ size_t และ ssize_t มีการเปลี่ยนแปลง ฉันไม่รู้ว่าอันไหน แต่% zd หยุดทำงานไปสักพักแล้ว นอกจากนี้ไม่มีรูปแบบในวันนี้ว่าจะพิมพ์ NSInteger โดยไม่มีการเตือนทั้งระบบ 32 และ 64 บิต

ดังนั้นสิ่งเดียวที่คุณสามารถทำได้น่าเสียดาย: ใช้% ld และโยนค่าของคุณจาก NSInteger เป็น long หรือจาก NSUInteger ไปเป็นแบบไม่ได้ลงนาม

เมื่อคุณไม่ได้สร้างเป็น 32 บิตอีกต่อไปคุณสามารถใช้% ld โดยไม่ต้องใช้แคสต์ใด ๆ


0

รูปแบบมาจากฟังก์ชัน UNIX / POSIX printf มาตรฐาน ใช้% luสำหรับที่ไม่ได้ลงชื่อยาว ,% ld นาน% แนวหน้านานนานและLLU%สำหรับที่ไม่ได้ลงชื่อนาน ลอง man printf บนคอนโซล แต่บน Mac มันไม่สมบูรณ์ การจัดการ linux มีความชัดเจนมากขึ้นhttp://www.manpages.info/linux/sprintf.3.html

คำเตือนทั้งสองสามารถแก้ไขได้โดย NSLog (@ "% lu", (unsigned long) arg); รวมกับการแคสต์เนื่องจากโค้ดจะถูกคอมไพล์เป็น 32 และ 64 บิตสำหรับ iOS มิฉะนั้นการรวบรวมแต่ละรายการจะสร้างคำเตือนแยกกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.