NSLog (& # 8230;) неправильный спецификатор формата влияет на другие переменные?

StackOverflow https://stackoverflow.com/questions/1229212

Вопрос

Недавно я потратил около получаса, чтобы отследить это странное поведение в 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 будет напечатано как нуль?

Это было полезно?

Решение

Вы только что испортили выравнивание памяти в вашем стеке. Я предполагаю, что вы используете новейший продукт 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 , вероятно, ничего не получит, а затем напечатает ( нуль).

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

Другие советы

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top