Подтвердить что ты не робот

Альтернативы типу casting при форматировании 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-prefixed и типичного для длинного. Тогда наш код выглядит примерно так:

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-битного перехода Apples, которые рекомендуют подход %lu (unsigned long). Я прошу альтернативу литью типа.

4b9b3361

Ответ 1

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

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

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

  • 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 имеет тот же размер поскольку указатель не считается безопасным). Это может сломаться в будущем.

Единственная альтернатива литейному типу, о которой я знаю, - это ответ на "Типы Foundation при компиляции для 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);

Ответ 2

Вместо этого я предпочитаю использовать NSNumber:

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

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

Ответ 3

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