2009-08-04 3 views
2

私は最近のNSLog(...)で、この奇妙な行動を追跡する約半分の時間を無駄に:NSLog(...)の不適切な書式指定子が他の変数に影響しますか?

NSString *text = @"abc"; 
long long num = 123; 
NSLog(@"num=%lld, text=%@",num,text); //(A) 
NSLog(@"num=%d, text=%@",num,text); //(B) 

ライン(A)は期待 "NUM = 123、テキスト= ABC" が、ライン(Bを出力します)は、「num = 123、text = (null)」と印刷します。

明らかに、long long%dと印刷するのは間違いですが、なぜtextがヌルとして印刷されるのか説明できますか?

+0

-Wallオプションでコンパイルすると、コンパイラはこのような問題について警告します。警告が常にビルドを破るように-Werrorも強く推奨します。 –

+1

@Adam Rosenfield、ala '-Wformat'という形式チェックのサポートは、gcc/objcでは常にちょっと変わっています。これはコンパイラのそれ以降のバージョンでうまくいくようですが、私はXcode 3.1の下でクイックチェックを行い、上記のエラーをキャッチしませんでした。 – johne

+0

-WformatはCの文字列(printfのような)にしか作用せず、NSString *オブジェクト定数(NSLogが使用する)を完全に解析できないため、エラーをキャッチしません。 –

答えて

9

あなたはスタック上でメモリアライメントを乱しただけです。あなたがx86プロセッサを搭載した最新のApple製品を使用していると仮定します。これらの仮定を考慮に入れて、両方の状況でスタックを考慮してください。

 
    |  stack   | first | second | 
    +---------------------+-------+--------+ 
    |  123   |  | %d | 
    +---------------------+ %lld +--------+ 
    |   0   |  | %@ | 
    +---------------------+-------+--------+ 
    | pointer to text | %@ |ignored | 
    +---------------------+-------+--------+ 

最初の状況では、スタックを8バイト、次に4バイトにします。そして、NSLogはスタック12バイト(%lldの場合は8バイト、%@の場合は4バイト)を取り戻すよう指示されます。

2番目の状況では、NSLogに最初に4バイト(%d)を渡すよう指示します。あなたの変数は8バイトの長さであり、本当に小さい数を保持するので、その上位4バイトは0になります。そして、NSLogがテキストを印刷しようとすると、スタックからnilが取られます。

Obj-Cではnilへのメッセージ送信が有効なので、NSLogはにnilを送信して、おそらく何も得ず、次にprint(null)します。

Objective-Cは追加されたばかりのCなので、呼び出し元はこの混乱を完全に取り除きます。

1

varargsの実装方法はシステムによって異なります。しかし、起こりそうなことは、引数が異なるサイズであっても、引数がバッファに連続して格納されるということです。したがって、引数の最初の8バイト(それはlong long intのサイズと仮定します)はlong long intで、次の4バイト(システム上のポインタのサイズを仮定)はNSStringポインタです。あなたはそれがintしてからポインタを受け取る関数を教えてくれたときに

はその後、それがポインタされる次の4バイト(つまりintの大きさだと仮定して)最初の4つのバイトがintことを期待しています。特定のエンディアンとシステム上の引数の配列のために、long long intの最初の4バイトはあなたの番号の最下位バイトであるため、123が出力されます。オブジェクトポインタの場合、次の4バイトが読み込まれます。この場合、あなたの数字の最上位バイトはすべて0であるので、それはnilポインターとして解釈されます。実際のポインタは決して読み込まれません。

関連する問題