Search code examples
objective-cformattingstackprintfnslog

NSLog(...) improper format specifier affects other variables?


I recently wasted about half an hour tracking down this odd behavior in NSLog(...):

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

Line (A) prints the expected "num=123, text=abc", but line (B) prints "num=123, text=(null)".

Obviously, printing a long long with %d is a mistake, but can someone explain why it would cause text to be printed as null?


Solution

  • You just messed up memory alignment on your stack. I assume than you use newest Apple product with x86 processor. Taking these assumptions into account your stack looks like that in both situations:

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

    In first situation you put on stack 8 bytes and then 4 bytes. And than NSLog is instructed to take back from stack 12 bytes (8 bytes for %lld and 4 bytes for %@).

    In second situation you instruct NSLog to first take 4 bytes (%d). Since your variable is 8 bytes long and holds really small number its upper 4 bytes will be 0. Then when NSLog will try to print text it will take nil from stack.

    Since sending message to nil is valid in Obj-C NSLog will just send description: to nil get probably nothing and then print (null).

    In the end since Objective-C is just C with additions, caller cleans up whole this mess.