NSLog (...) неправильный спецификатор формата влияет на другие переменные? - PullRequest
2 голосов
/ 04 августа 2009

Недавно я потратил около получаса, чтобы отследить это странное поведение в 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, text = abc", но строка (B) печатает "num = 123, text = (null) ".

Очевидно, что печать long long с %d является ошибкой, но кто-то может объяснить, почему это приведет к тому, что text будет напечатано как ноль?

Ответы [ 2 ]

9 голосов
/ 04 августа 2009

Вы только что испортили выравнивание памяти в вашем стеке. Я предполагаю, что вы используете новейший продукт Apple с процессором x86. С учетом этих предположений ваш стек выглядит так в обеих ситуациях:

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

В первой ситуации вы кладете в стек 8 байтов, а затем 4 байта. И затем NSLog получает команду извлечь из стека 12 байтов (8 байтов для %lld и 4 байта для %@).

Во второй ситуации вы указываете NSLog сначала взять 4 байта (%d). Поскольку ваша переменная имеет длину 8 байт и содержит действительно небольшое число, ее верхние 4 байта будут равны 0. Тогда, когда NSLog попытается напечатать текст, он возьмет nil из стека.

Поскольку отправка сообщения на nil действительна в Obj-C, NSLog просто отправит description: на nil, вероятно, ничего не получит, а затем выведет (null).

В конце концов, поскольку Objective-C - это просто C с добавлениями, вызывающая сторона устраняет весь этот беспорядок.

1 голос
/ 04 августа 2009

Способ реализации varargs зависит от системы. Но, вероятно, происходит то, что аргументы хранятся последовательно в буфере, даже если аргументы могут быть разных размеров. Таким образом, первые 8 байтов (при условии, что это размер long long int) аргументов - это long long int, а следующие 4 байта (при условии, что это размер указателя в вашей системе) - это указатель NSString.

Затем, когда вы сообщаете функции, что она ожидает int, а затем указатель, она ожидает, что первые 4 байта будут int (при условии, что это размер int), а следующие 4 байта быть указателем Из-за особой последовательности и порядка аргументов в вашей системе первые 4 байта long long int являются наименее значимыми байтами вашего числа, поэтому он печатает 123. Затем для указателя объекта он читает следующие 4 байта. , что в данном случае является наиболее значимым байтом вашего числа, которое равно всем 0, поэтому интерпретируется как указатель nil. Фактический указатель никогда не читается.

...