Альтернативы приведению типов при форматировании NS(U)Integer на 32- и 64-битных архитектурах?

С 64-битной версией iOS мы не можем использовать %d а также %u больше форматировать NSInteger а также NSUInteger, Потому что для 64-битных это typedef'd для long а также unsigned long вместо int а также unsigned int,

Так что XCode выдаст предупреждения, если вы попытаетесь отформатировать NSInteger с% d. Xcode хорош для нас и предлагает замену для этих двух случаев, которая состоит из спецификатора формата с префиксом l и типа type для long. Тогда наш код в основном выглядит так:

NSLog(@"%ld", (long)i);
NSLog(@"%lu", (unsigned long)u);

Что, если вы спросите меня, это боль в глазу.

Пару дней назад кто-то в Twitter упомянул спецификаторы формата %zd отформатировать подписанные переменные и %tu форматировать переменные без знака на 32- и 64-битных платформах.

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

Который, кажется, работает. И который мне нравится больше, чем доработка.

Но я, честно говоря, понятия не имею, почему они работают. Прямо сейчас оба - в основном магические ценности для меня.

Я провел небольшое исследование и выяснил, что z префикс означает, что следующий спецификатор формата имеет тот же размер, что и size_t, Но я понятия не имею, что такое префикс t средства. Итак, у меня есть два вопроса:

Что именно делать %zd а также %tu имею в виду?

И безопасно ли использовать %zd а также %tu вместо яблок предложение по типу продлить?


Мне известны аналогичные вопросы и руководства по 64-битному переходу для Apple, которые все рекомендуют %lu (unsigned long) подход. Я прошу альтернативу приведению типа.

3 ответа

Решение

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

  • Z
    Указывает, что следующий [...] спецификатор преобразования применяется к size_t или соответствующий знаковый аргумент целочисленного типа;
  • T
    Указывает, что следующий [...] спецификатор преобразования применяется к ptrdiff_t или соответствующий аргумент типа unsigned;

И из http://en.wikipedia.org/wiki/Size_t:

  • size_t используется для представления размера любого объекта (включая массивы) в конкретной реализации. Используется в качестве типа возврата sizeof оператор.
  • ptrdiff_t используется для представления разницы между указателями.

На текущих платформах OS X и iOS у нас есть

typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;

где __SIZE_TYPE__ а также __PTRDIFF_TYPE__ предопределены компилятором. Для 32-битного компилятор определяет

#define __SIZE_TYPE__ long unsigned int
#define __PTRDIFF_TYPE__ int

а для 64-битной компилятор определяет

#define __SIZE_TYPE__ long unsigned int
#define __PTRDIFF_TYPE__ long int

(Это могло измениться между версиями Xcode. По мотивам комментария @user102008, я проверил это с помощью Xcode 6.2 и обновил ответ.)

Так ptrdiff_t а также NSInteger оба typedef'd для одного и того же типа: int на 32-битной и long на 64-битной. Следовательно

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

работать правильно и компилировать без предупреждений на всех текущих платформах iOS и OS X.

size_t а также NSUInteger имеют одинаковый размер на всех платформах, но они не одного типа, поэтому

NSLog(@"%zu", u);

на самом деле выдает предупреждение при компиляции для 32-битной.

Но это соотношение не зафиксировано ни в одном стандарте (насколько я знаю), поэтому я не считаю его безопасным (в том же смысле, что и предположим, что long имеет такой же размер, как указатель (не считается безопасным). Это может сломаться в будущем.

Единственная альтернатива приведению типов, которую я знаю, - это ответ на " Типы основ при компиляции для arm64 и 32-битной архитектуры " с использованием макросов препроцессора:

// 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);

Я предпочитаю просто использовать NSNumber вместо:

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

Это работает не во всех ситуациях, но я заменил большую часть форматирования моего целочисленного значения NS(U) приведенным выше.

Согласно Построению 32-битного Подобно 64-битному, другое решение состоит в том, чтобы определить NS_BUILD_32_LIKE_64 макрос, а затем вы можете просто использовать %ld а также %lu спецификаторы с NSInteger а также NSUInteger без кастинга и без предупреждений.

Другие вопросы по тегам