Alternatives to type casting when formatting NS(U)Integer on 32 and 64 bit architectures?

前端 未结 3 1798
粉色の甜心
粉色の甜心 2020-12-12 13:39

With the 64 bit version of iOS we can\'t use %d and %u anymore to format NSInteger and NSUInteger. Because for 64 bit tho

相关标签:
3条回答
  • 2020-12-12 14:13

    From http://pubs.opengroup.org/onlinepubs/009695399/functions/printf.html:

    • z
      Specifies that a following [...] conversion specifier applies to a size_t or the corresponding signed integer type argument;
    • t
      Specifies that a following [...] conversion specifier applies to a ptrdiff_t or the corresponding unsigned type argument;

    And from http://en.wikipedia.org/wiki/Size_t#Size_and_pointer_difference_types:

    • size_t is used to represent the size of any object (including arrays) in the particular implementation. It is used as the return type of the sizeof operator.
    • ptrdiff_t is used to represent the difference between pointers.

    On the current OS X and iOS platforms we have

    typedef __SIZE_TYPE__ size_t;
    typedef __PTRDIFF_TYPE__ ptrdiff_t;
    

    where __SIZE_TYPE__ and __PTRDIFF_TYPE__ are predefined by the compiler. For 32-bit the compiler defines

    #define __SIZE_TYPE__ long unsigned int
    #define __PTRDIFF_TYPE__ int
    

    and for 64-bit the compiler defines

    #define __SIZE_TYPE__ long unsigned int
    #define __PTRDIFF_TYPE__ long int
    

    (This may have changed between Xcode versions. Motivated by @user102008's comment, I have checked this with Xcode 6.2 and updated the answer.)

    So ptrdiff_t and NSInteger are both typedef'd to the same type: int on 32-bit and long on 64-bit. Therefore

    NSLog(@"%td", i);
    NSLog(@"%tu", u);
    

    work correctly and compile without warnings on all current iOS and OS X platforms.

    size_t and NSUInteger have the same size on all platforms, but they are not the same type, so

    NSLog(@"%zu", u);
    

    actually gives a warning when compiling for 32-bit.

    But this relation is not fixed in any standard (as far as I know), therefore I would not consider it safe (in the same sense as assuming that long has the same size as a pointer is not considered safe). It might break in the future.

    The only alternative to type casting that I know of is from the answer to "Foundation types when compiling for arm64 and 32-bit architecture", using preprocessor macros:

    // In your prefix header or something
    #if __LP64__
    #define NSI "ld"
    #define NSU "lu"
    #else
    #define NSI "d"
    #define NSU "u"
    #endif
    
    NSLog(@"i=%"NSI, i);
    NSLog(@"u=%"NSU, u);
    
    0 讨论(0)
  • 2020-12-12 14:23

    According to Building 32-bit Like 64-bit, another solution is to define the NS_BUILD_32_LIKE_64 macro, and then you can simply use the %ld and %lu specifiers with NSInteger and NSUInteger without casting and without warnings.

    0 讨论(0)
  • 2020-12-12 14:29

    I prefer to just use an NSNumber instead:

    NSInteger myInteger = 3;
    NSLog(@"%@", @(myInteger));
    

    This does not work in all situations, but I've replaced most of my NS(U)Integer formatting with the above.

    0 讨论(0)
提交回复
热议问题