web-dev-qa-db-ja.com

32ビットおよび64ビットアーキテクチャでNS(U)Integerをフォーマットするときの型キャストの代替案?

IOSの64ビットバージョンでは、_%d_および_%u_を使用してNSIntegerおよびNSUIntegerをフォーマットすることはできなくなりました。 64ビットの場合、これらはlongおよび_unsigned long_ではなくintおよび_unsigned int_にtypedefされるためです。

したがって、NSIntegerを%dでフォーマットしようとすると、Xcodeは警告をスローします。 Xcodeは私たちにとって素晴らしいものであり、l接頭辞付きのフォーマット指定子とlongへの型キャストで構成される、これら2つのケースの代わりを提供します。次に、コードは基本的に次のようになります。

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

あなたが私に尋ねれば、それは目の痛みです。

数日前、Twitterの誰かが、32ビットおよび64ビットのプラットフォームで、符号付き変数をフォーマットするためのフォーマット指定子_%zd_および符号なし変数をフォーマットするための_%tu_について言及しました。

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

うまくいくようです。そして、私はタイプキャストよりも好きです。

しかし、正直なところ、なぜそれらが機能するのかわかりません。今のところ、どちらも基本的に私にとって魔法の値です。

少し調べてみたところ、zプレフィックスは、次の形式指定子が_size_t_と同じサイズであることを意味することがわかりました。しかし、プレフィックスtの意味がまったくわかりません。だから私は2つの質問があります:

_%zd_および_%tu_は正確にはどういう意味ですか?

そして、Appleの提案の代わりに_%zd_と_%tu_を使用してlongに型キャストするのは安全ですか?


同様の質問とAppleの64ビット移行ガイドを知っています。これらはすべて%lu (unsigned long)アプローチを推奨しています。型キャストの代替手段を求めています。

53
Matthias Bauch

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

  • z
    次の[...]変換指定子がsize_tまたは対応する符号付き整数型の引数に適用されることを指定します。
  • 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_tNSIntegerはどちらもと同じタイプにtypedefされます。32ビットではintlong 64ビット。したがって

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

正常に動作し、現在のすべてのiOSおよびOS Xプラットフォームで警告なしにコンパイルします。

size_tNSUIntegerはすべてのプラットフォームで同じサイズですが、同じタイプではないため、

NSLog(@"%zu", u);

32ビット向けにコンパイルすると、実際には警告が表示されます。

しかし、この関係は(私の知る限り)どの標準でも修正されていないため、not安全であると考えます(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);
52
Martin R

代わりにNSNumberを使用することを好みます:

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

これはすべての状況で機能するわけではありませんが、NS(U)Integerフォーマットのほとんどを上記のものに置き換えました。

11

64ビットのような32ビットのビルド によると、別の解決策はNS_BUILD_32_LIKE_64マクロを使用すると、%ldおよび%lu指定子とNSIntegerおよびNSUIntegerを使用したキャストおよび警告なし。

2
user102008