Альтернативы приведению типов при форматировании 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
без кастинга и без предупреждений.