Вычитание указателей: откуда берется этот недостающий уровень косвенности? - PullRequest
1 голос
/ 21 июля 2010

У меня проблемы с пониманием поведения компилятора MS VC на этом. Эта строка компилируется нормально, но результат, который я получаю, совсем не тот, который я ожидал:

this->Test((char *)&CS2 - (char *)&CS1 == sizeof(void *));

Аргументы CS1 и CS2 объявляются следующим образом:

myFunction(tCS1* CS1, tCS2* CS2) {...

tCS1 и tCS2 - структуры, содержащие один int и один __int64, соответственно

Это предназначено для проверки расстояния в стеке между моими аргументами CS1 и CS2, которые являются указателями. Когда я прерываю выполнение в этой строке и использую отладчик для получения адресов моих двух переменных, я обнаруживаю, что они действительно на расстоянии 8 байт друг от друга (платформа x64).

Однако результат сравнения неверен.

Вот код сборки, сгенерированный компилятором:

mov         rax,qword ptr [CS1] 
mov         rdi,qword ptr [CS2] 
sub         rdi,rax 

(затем он выполняет сравнение, используя результат, сохраненный в rdi, и выполняет вызов)

Да, компилятор сравнивает значения аргументов моего указателя, а не их адреса. Я пропускаю уровень косвенности здесь, куда это пошло?

Конечно, я не могу воспроизвести это в тестовой среде, и я понятия не имею, где искать больше. Я кросс-компилирую этот бит кода на 32-битной машине на платформу x64 (я должен), это единственная «странная вещь» в этом. Любая идея, любой намек?

Ответы [ 2 ]

0 голосов
/ 23 июля 2010

Как отметили @jpalacek и комментаторы, это не определено, и компилятор может воспользоваться этим, чтобы делать все, что угодно.Это довольно странно.

Этот код "работает" на gcc:


#include 

int func(int *a, int *b)
{
    return (char *)&a - (char *) &b;
}

int main(void)
{
    int a, b;
    printf("%d", func(&a, &b));
    return 0;
}

(gdb) disassemble func
Dump of assembler code for function func:
   0x0 80483e4 :    push   %ebp
   0x080483e5 : mov    %esp,%ebp
=> 0x080483e7 : lea    0x8(%ebp),%edx
   0x080483ea : lea    0xc(%ebp),%eax
   0x080483ed : mov    %edx,%ecx
   0x080483ef :    sub    %eax,%ecx
   0x080483f1 :    mov    %ecx,%eax
   0x080483f3 :    pop    %ebp
   0x080483f4 :    ret    
End of assembler dump.

, и при оптимизации он просто знает их относительные адреса:

(edit:ответ здесь почему-то урезан)

(gdb) disassemble  func
Dump of assembler code for function func:
   0x08048410 : push   %ebp
   0x08048411 : mov    $0xfffffffc,%eax
   0x08048416 : mov    %esp,%ebp
   0x08048418 : pop    %ebp
   0x08048419 : ret    
End of assembler dump.

Интересно, что при оптимизации -O4 возвращается +4, а без него - -4.

Почемуты пытаешься сделать это так или иначе?В общем, нет никакой гарантии, что аргументы имеют какой-либо адрес памяти: они могут передаваться в регистрах.

0 голосов
/ 21 июля 2010

Сборка

mov         rax,qword ptr [CS1] 
mov         rdi,qword ptr [CS2] 
sub         rdi,rax

указывает, что CS1 и CS2 на самом деле не аргументы стека, а скорее глобальные символы - если бы я хотел получить аналогичные результаты, я бы сделал что-то вроде этого:

int* CS1 = NULL, *CS2 = NULL; /* or any other value...*/
#define CS1 *CS1
#define CS2 *CS2

Конечно, это ужасный код - но вы проверяли, что в вашем коде нет таких вещей?Кроме того, динамический компоновщик может играть роль в этом.

И последнее, но не менее важное: если вы пытаетесь написать код вроде:

void foo()
{
  int a;
  int b;
  printf("%d", &a-&b);
}

Вы должны знать, что это на самом деле неопределенное поведение, поскольку C (и C ++) позволяет только вычитать указатели, указывающие внутри одного объекта (например, массива).

...